summaryrefslogtreecommitdiffstats
path: root/Src/IP/MostMsg.cpp
blob: 7148d2f3f401ca005386171cbcce6a7b4cdacef8 (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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/*
 * Video On Demand Samples
 *
 * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * You may also obtain this software under a propriety license from Microchip.
 * Please contact Microchip for further information.
 *
 */

#include "Console.h"
#include "MostMsg.h"
#include <stdlib.h>

static uint32_t crc32_tab[] =
{
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

static uint32_t CalcCRC32( const uint8_t *p, uint32_t size )
{
    uint32_t crc = ~0U;

    while( size-- )
        crc = crc32_tab[( crc ^ *p++ ) & 0xFF] ^ ( crc >> 8 );

    return crc ^ ~0U;
}


CMostMsg::CMostMsg() : m_nIsValid( false ), m_nRefCount( 1 ), m_nFBlock( 0 ), m_nFunc( 0 ), m_nInst( 0 ),
    m_nOpType( 0 ), m_nPayloadLen( 0 ), m_Payload( NULL ), m_nParsePos( 0 ),
    m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 )
{
}

CMostMsg::CMostMsg( uint32_t nFBlock, uint32_t nInst, uint32_t nFunc, uint8_t nOpType, uint32_t nPayloadLen,
    const uint8_t *Payload ) : m_nIsValid( true ), m_nRefCount( 1 ), m_Payload( NULL ),
    m_nParsePos( 0xFFFFFFFF ), m_nHeaderCrc( 0 ), m_nPayloadCrc( 0 )
{
    m_nFBlock = nFBlock;
    m_nInst = nInst;
    m_nFunc = nFunc;
    m_nOpType = nOpType;
    m_nPayloadLen = nPayloadLen;
    if( nPayloadLen > 0 )
    {
        m_Payload = ( uint8_t * )malloc( m_nPayloadLen );
        if( NULL == m_Payload )
        {
            ConsolePrintf(
                PRIO_ERROR, RED"CMostMsg Constructor failed to allocated %d bytes of memory"RESETCOLOR"\n", m_nPayloadLen );
            m_nIsValid = false;
            m_nPayloadLen = 0;
            return;
        }
        memcpy( m_Payload, Payload, nPayloadLen );
    }
}

CMostMsg::~CMostMsg()
{
    m_nIsValid = false;
    m_nPayloadLen = 0;
    if( NULL != m_Payload )
    {
        free( m_Payload );
        m_Payload = NULL;
    }
}

void CMostMsg::AddReference()
{
    ++m_nRefCount;
}

void CMostMsg::RemoveReference()
{
    if( 0 == --m_nRefCount )
        delete this;
}

uint32_t CMostMsg::ToByteArray( uint8_t **ppBuffer )
{
    if( NULL == ppBuffer )
    {
        ConsolePrintf(
            PRIO_ERROR, RED"CMostMsg::ToByteArray ppBuffer is NULL < %d"RESETCOLOR"\n" );
        return 0;
    }
    uint32_t serializedLen = 20;
    if( 0 != m_nPayloadLen )
        serializedLen += m_nPayloadLen + 4; //If there is payload, it will be CRC checked
    uint8_t *b = ( uint8_t * )malloc( serializedLen );
    if( NULL == b )
    {
        ConsolePrintf(
            PRIO_ERROR, RED"CMostMsg::ToByteArray failed to allocate %d bytes"RESETCOLOR"\n", ( m_nPayloadLen + 24 ) );
        *ppBuffer = NULL;
        return 0;
    }
    *ppBuffer = b;
    b[0] = ( uint8_t )0x49;
    b[1] = ( uint8_t )0x72;
    b[2] = ( uint8_t )0x16;
    b[3] = ( uint8_t )0x25;
    b[4] = ( uint8_t )( m_nFBlock & 0xFF );
    b[5] = ( uint8_t )( m_nInst & 0xFF );
    b[6] = ( uint8_t )( ( m_nFunc >> 8 ) & 0xFF );
    b[7] = ( uint8_t )( ( m_nFunc )&0xFF );
    b[8] = ( uint8_t )( m_nOpType );
    b[9] = ( uint8_t )0x0;
    b[10] = ( uint8_t )0x0;
    b[11] = ( uint8_t )0x0;
    b[12] = ( uint8_t )( ( m_nPayloadLen >> 24 ) & 0xFF );
    b[13] = ( uint8_t )( ( m_nPayloadLen >> 16 ) & 0xFF );
    b[14] = ( uint8_t )( ( m_nPayloadLen >> 8 ) & 0xFF );
    b[15] = ( uint8_t )( m_nPayloadLen & 0xFF );

    uint32_t headerCrc = CalcCRC32( b, 16 );

    b[16] = ( uint8_t )( ( headerCrc >> 24 ) & 0xFF );
    b[17] = ( uint8_t )( ( headerCrc >> 16 ) & 0xFF );
    b[18] = ( uint8_t )( ( headerCrc >> 8 ) & 0xFF );
    b[19] = ( uint8_t )( headerCrc & 0xFF );

    if( 0 != m_nPayloadLen )
    {
        for( uint32_t i = 0; i < m_nPayloadLen; i++ )
            b[20 + i] = m_Payload[i];

        uint32_t payloadCrc = CalcCRC32( &b[20], m_nPayloadLen );

        b[20 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 24 ) & 0xFF );
        b[21 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 16 ) & 0xFF );
        b[22 + m_nPayloadLen] = ( uint8_t )( ( payloadCrc >> 8 ) & 0xFF );
        b[23 + m_nPayloadLen] = ( uint8_t )( payloadCrc & 0xFF );
    }
    return serializedLen;
}

bool CMostMsg::Parse( uint8_t receivedByte )
{
    if( 0xFFFFFFFF == m_nParsePos )
    {
        ConsolePrintf(
            PRIO_ERROR, RED"CMostMsg::Parse was called, but the parse state is invalid"RESETCOLOR"\n" );
        return false;
    }
    bool valid = true;
    if( m_nParsePos <= 19 )
    {
        if( m_nParsePos <= 15 )
            m_zHeader[m_nParsePos] = receivedByte;

        //Check Header
        switch( m_nParsePos )
        {
        case 0:
            if( 0x49 != receivedByte )
                valid = false;
            break;
        case 1:
            if( 0x72 != receivedByte )
                valid = false;
            break;
        case 2:
            if( 0x16 != receivedByte )
                valid = false;
            break;
        case 3:
            if( 0x25 != receivedByte )
                valid = false;
            break;
        case 4:
            m_nFBlock = receivedByte;
            break;
        case 5:
            m_nInst = receivedByte;
            break;
        case 6:
            m_nFunc = receivedByte << 8;
            break;
        case 7:
            m_nFunc += receivedByte;
            break;
        case 8:
            m_nOpType = receivedByte;
            break;
        case 9:
        case 10:
        case 11:
            //Reserved for future use cases
            break;
        case 12:
            m_nPayloadLen = receivedByte << 24;
            break;
        case 13:
            m_nPayloadLen += receivedByte << 16;
            break;
        case 14:
            m_nPayloadLen += receivedByte << 8;
            break;
        case 15:
            m_nPayloadLen += receivedByte;
            break;
        case 16:
            m_nHeaderCrc = receivedByte << 24;
            break;
        case 17:
            m_nHeaderCrc += receivedByte << 16;
            break;
        case 18:
            m_nHeaderCrc += receivedByte << 8;
            break;
        case 19:
            m_nHeaderCrc += receivedByte;
            if( m_nHeaderCrc == CalcCRC32( m_zHeader, 16 ) )
            {
                if( 0 != m_nPayloadLen )
                {
                    //Continue to parse the payload
                    m_Payload = ( uint8_t * )malloc( m_nPayloadLen );
                    if( NULL == m_Payload )
                    {
                        ConsolePrintf(
                            PRIO_ERROR, RED"MostMsg::Protocol deserializing error, could not allocate %d bytes of memory"RESETCOLOR
                            "\n", m_nPayloadLen );
                        valid = false;
                        m_nPayloadLen = 0;
                    }
                }
                else
                {
                    //No payload, we are finished
                    m_nIsValid = true;
                    m_nParsePos = 0xFFFFFFFF - 1;
                }
            }
            else
            {
                ConsolePrintf(
                    PRIO_ERROR, RED"MostMsg::Protocol error, header CRC does not match"RESETCOLOR"\n" );
                valid = false;
            }
            break;
        }
    }
    else
    {
        if( m_nParsePos < ( m_nPayloadLen + 20 ) )
        {
            //Check Payload and store it
            if( NULL != m_Payload )
            {
                m_Payload[m_nParsePos - 20] = receivedByte;
            }
            else
            {
                ConsolePrintf(
                    PRIO_ERROR, RED"MostMsg::Protocol deserializing error, payload buffer is NULL"RESETCOLOR"\n" );
                valid = false;
                m_nPayloadLen = 0;
            }
        }
        else
        {
            //Finally check CRC of payload
            uint32_t crcState = m_nParsePos - m_nPayloadLen - 20;
            switch( crcState )
            {
            case 0:
                m_nPayloadCrc = receivedByte << 24;
                break;
            case 1:
                m_nPayloadCrc += receivedByte << 16;
                break;
            case 2:
                m_nPayloadCrc += receivedByte << 8;
                break;
            case 3:
                m_nPayloadCrc += receivedByte;
                if( m_nPayloadCrc == CalcCRC32( m_Payload, m_nPayloadLen ) )
                {
                    //Payload is successfully received
                    m_nIsValid = true;
                    m_nParsePos = 0xFFFFFFFF - 1;
                }
                else
                {
                    ConsolePrintf(
                        PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC mismatch"RESETCOLOR"\n" );
                    valid = false;
                }
                break;
            default:
                ConsolePrintf(
                    PRIO_ERROR, RED"MostMsg::Protocol deserializing error, Payload CRC state is out of range"RESETCOLOR"\n" );
                valid = false;
            }
        }
    }

    if( valid )
    {
        ++m_nParsePos;
    }
    else
    {
        ConsolePrintf(
            PRIO_ERROR, RED"CMostMsg::Parse failed at parse position %d"RESETCOLOR"\n", m_nParsePos );
        m_nParsePos = 0xFFFFFFFF;
    }
    return valid;
}

bool CMostMsg::IsValid()
{
    return m_nIsValid;
}

uint32_t CMostMsg::GetFBlock()
{
    return m_nFBlock;
}

uint32_t CMostMsg::GetFunc()
{
    return m_nFunc;
}

uint32_t CMostMsg::GetInst()
{
    return m_nInst;
}

uint8_t CMostMsg::GetOpType()
{
    return m_nOpType;
}

uint8_t *CMostMsg::GetPayload()
{
    return m_Payload;
}

uint32_t CMostMsg::GetPayloadLen()
{
    return m_nPayloadLen;
}
= streamID) { iAccessControl = AHL_ACCESS_CONTROL_GRANTED; } } } return iAccessControl; } static int CreateEvents() { g_AHLCtx.policyCtx.propertyEvent = afb_daemon_make_event(AHL_ENDPOINT_PROPERTY_EVENT); int err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent); if (err) { AFB_ERROR("Could not create endpoint property change event"); return AHL_FAIL; } g_AHLCtx.policyCtx.volumeEvent = afb_daemon_make_event(AHL_ENDPOINT_VOLUME_EVENT); err = !afb_event_is_valid(g_AHLCtx.policyCtx.volumeEvent); if (err) { AFB_ERROR("Could not create endpoint volume change event"); return AHL_FAIL; } g_AHLCtx.policyCtx.postActionEvent = afb_daemon_make_event(AHL_POST_ACTION_EVENT); err = !afb_event_is_valid(g_AHLCtx.policyCtx.postActionEvent); if (err) { AFB_ERROR("Could not create post action event call event"); return AHL_FAIL; } return AHL_SUCCESS; } static void AhlBindingTerm() { #ifndef AHL_DISCONNECT_POLICY // Policy termination Policy_Term(); #endif // Roles if (g_AHLCtx.policyCtx.pRoleInfo != NULL) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, g_AHLCtx.policyCtx.pRoleInfo); while (g_hash_table_iter_next (&iter, &key, &value)) { RoleInfoT * pRole = (RoleInfoT *)value; if (pRole) { if(pRole->pRoleName) { g_free(pRole->pRoleName); pRole->pRoleName = NULL; } // Actions if (pRole->pActionList) { for (int i = 0; i < pRole->pActionList->len; i++) { g_ptr_array_remove_index( pRole->pActionList, i ); // Free char * is called by GLib } } // Source endpoints if (pRole->pSourceEndpoints) { g_ptr_array_unref(pRole->pSourceEndpoints); } // Sink endpoints if (pRole->pSinkEndpoints) { g_ptr_array_unref(pRole->pSinkEndpoints); } free(pRole); } } g_hash_table_remove_all(g_AHLCtx.policyCtx.pRoleInfo); g_hash_table_destroy(g_AHLCtx.policyCtx.pRoleInfo); g_AHLCtx.policyCtx.pRoleInfo = NULL; } if (g_AHLCtx.policyCtx.pStreams) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pStreams); while (g_hash_table_iter_next (&iter, &key, &value)) { if (value) free(value); } g_hash_table_remove_all(g_AHLCtx.policyCtx.pStreams); g_hash_table_destroy(g_AHLCtx.policyCtx.pStreams); g_AHLCtx.policyCtx.pStreams = NULL; } if (g_AHLCtx.policyCtx.pHALList) { g_ptr_array_free(g_AHLCtx.policyCtx.pHALList,TRUE); g_AHLCtx.policyCtx.pHALList = NULL; } AFB_INFO("Audio high-level binding termination success"); } // Binding initialization PUBLIC int AhlBindingInit() { memset(&g_AHLCtx,0,sizeof(g_AHLCtx)); // Register exit function atexit(AhlBindingTerm); // Create AGL Events int err = CreateEvents(); if(err) { // Error messages already reported inside CreateEvents return AHL_FAIL; } // Parse high-level binding JSON configuration file (will build device lists) err = ParseHLBConfig(); if(err) { // Error messages already reported inside ParseHLBConfig return AHL_FAIL; } #ifndef AHL_DISCONNECT_POLICY // Policy initialization err = Policy_Init(); if(err == AHL_POLICY_REJECT) { //Error messages already reported inside PolicyInit return AHL_FAIL; } // Call policy for initalization of all source + sink endpoints for all audio Roles GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); while (g_hash_table_iter_next (&iter, &key, &value)) { RoleInfoT * pRoleInfo = (RoleInfoT*)value; if (pRoleInfo->pSourceEndpoints){ // for all source endpoints for (int j = 0; j < pRoleInfo->pSourceEndpoints->len; j++) { EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSourceEndpoints,j); g_assert_nonnull(pCurEndpointInfo); json_object *pInPolicyEndpointJ = NULL; err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); if (err) { AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); return AHL_FAIL; } else { json_object * pOutPolicyEndpointJ = NULL; err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); if (err == AHL_POLICY_REJECT) { AFB_WARNING("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); // continue } json_object_put(pInPolicyEndpointJ); err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); if (err) { AFB_ERROR("Policy endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); return AHL_FAIL; } // json_object_put(pOutPolicyEndpointJ); } } } if (pRoleInfo->pSinkEndpoints){ // for all sink endpoints for (int j = 0; j < pRoleInfo->pSinkEndpoints->len; j++) { EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSinkEndpoints,j); g_assert_nonnull(pCurEndpointInfo); json_object *pInPolicyEndpointJ = NULL; err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); if (err) { AFB_ERROR("Unable to Create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); return AHL_FAIL; } else { json_object *pOutPolicyEndpointJ = NULL; err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); if (err == AHL_POLICY_REJECT) { AFB_WARNING("Policy endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); // continue } json_object_put(pInPolicyEndpointJ); err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); if (err) { AFB_ERROR("Policy endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); return AHL_FAIL; } //json_object_put(pOutPolicyEndpointJ); } } } } #endif // AHL_DISCONNECT_POLICY // Initialize list of active streams g_AHLCtx.policyCtx.pStreams = g_hash_table_new(g_int_hash, g_int_equal); if(g_AHLCtx.policyCtx.pStreams == NULL) { AFB_ERROR("Unable to create Active Stream List"); return AHL_FAIL; } AFB_DEBUG("Audio high-level Binding success"); return AHL_SUCCESS; } PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) { AFB_DEBUG("AHL received event %s", evtname); // TODO: Handle event from the policy to update internal information (currently not possible since within the same binding) #ifndef AHL_DISCONNECT_POLICY // Temp: currently forward to policy to handle events (they will be received directly when disconnected into separate binding) Policy_OnEvent(evtname, eventJ); #endif } PUBLIC void audiohlapi_get_endpoints(struct afb_req req) { json_object *devicesJ = NULL; json_object *deviceJ = NULL; json_object *queryJ = NULL; char * audioRole = NULL; char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:s}", "audio_role", &audioRole,"endpoint_type",&pEndpointTypeStr); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } endpointType = EndpointTypeToEnum(pEndpointTypeStr); RoleInfoT * pRole = GetRole(audioRole); if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ)); return; } else { devicesJ = json_object_new_array(); GPtrArray * pDeviceArray = NULL; if (endpointType == ENDPOINTTYPE_SOURCE) pDeviceArray = pRole->pSourceEndpoints; else pDeviceArray = pRole->pSinkEndpoints; if (pDeviceArray) { int iNumberDevices = pDeviceArray->len; for ( int j = 0 ; j < iNumberDevices; j++) { EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j); if (pEndpointInfo) { JSONPublicPackageEndpoint(pEndpointInfo,&deviceJ); json_object_array_add(devicesJ, deviceJ); } } } } afb_req_success(req, devicesJ, "List of endpoints"); } PUBLIC void audiohlapi_stream_open(struct afb_req req) { json_object *streamInfoJ = NULL; StreamInfoT * pStreamInfo = NULL; json_object *queryJ = NULL; char * audioRole = NULL; char * endpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; endpointID_t endpointID = AHL_UNDEFINED; EndpointInfoT * pEndpointInfo = NULL; EndpointSelectionModeT endpointSelMode = ENDPOINTSELMODEMAXVALUE; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointTypeStr, "endpoint_id", &endpointID); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } endpointType = EndpointTypeToEnum(endpointTypeStr); // Check if there is already an existing context for this client AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { pClientCtx = AllocateClientContext(); afb_req_context_set(req, pClientCtx, TerminateClientContext); } RoleInfoT * pRole = GetRole(audioRole); if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } GPtrArray * pDeviceArray = NULL; if (endpointType == ENDPOINTTYPE_SOURCE){ pDeviceArray = pRole->pSourceEndpoints; } else{ pDeviceArray = pRole->pSinkEndpoints; } if (pDeviceArray == NULL || pDeviceArray->len == 0) { afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%s",audioRole,endpointTypeStr); return; } if (endpointID == AHL_UNDEFINED) { // Assign a device based on configuration priority (first in the list for requested role and endpoint type) pEndpointInfo = g_ptr_array_index(pDeviceArray,0); endpointSelMode = ENDPOINTSELMODE_AUTO; } else{ endpointSelMode = ENDPOINTSELMODE_MANUAL; // Find specified endpoint ID in list of devices int iNumberDevices = pDeviceArray->len; for ( int j = 0 ; j < iNumberDevices; j++) { pEndpointInfo = g_ptr_array_index(pDeviceArray,j); if (pEndpointInfo && pEndpointInfo->endpointID == endpointID) { break; } pEndpointInfo = NULL; } } if (pEndpointInfo == NULL) { afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID); return; } pStreamInfo = (StreamInfoT*) malloc(sizeof(StreamInfoT)); memset(pStreamInfo,0,sizeof(StreamInfoT)); // Create stream pStreamInfo->streamID = CreateNewStreamID(); // create new ID pStreamInfo->streamState = STREAM_STATE_IDLE; pStreamInfo->streamMute = STREAM_UNMUTED; pStreamInfo->pEndpointInfo = pEndpointInfo; pStreamInfo->endpointSelMode = endpointSelMode; // Directly from role config for now, but could be programmatically overriden in the future pStreamInfo->pRoleName = pRole->pRoleName; pStreamInfo->iPriority = pRole->iPriority; pStreamInfo->eInterruptBehavior = pRole->eInterruptBehavior; #ifndef AHL_DISCONNECT_POLICY // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions json_object *pPolicyStreamJ = NULL; err = StreamInfoToJSON(pStreamInfo,&pPolicyStreamJ); if (err) { afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_OpenStream"); return; } int policyAllowed = Policy_OpenStream(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context"); return; } #endif char streamEventName[128]; snprintf(streamEventName,128,"ahl_streamstate_%d",pStreamInfo->streamID); pStreamInfo->streamStateEvent = afb_daemon_make_event(streamEventName); err = !afb_event_is_valid(pStreamInfo->streamStateEvent); if (err) { afb_req_fail(req, "Stream event creation failure", "Could not create stream specific state change event"); return; } err = afb_req_subscribe(req,pStreamInfo->streamStateEvent); if (err) { afb_req_fail(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event"); return; } // Add to client context stream ID access rights g_array_append_val(pClientCtx->pStreamAccessList, pStreamInfo->streamID); // Push stream on active stream list if (g_AHLCtx.policyCtx.pStreams) g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo ); // Package and return stream information to client JSONPublicPackageStream(pStreamInfo,&streamInfoJ); afb_req_success(req, streamInfoJ, "Stream info structure"); } PUBLIC void audiohlapi_stream_close(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s?i}", "stream_id", &streamID); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } // Check if there is already an existing context for this client AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); return; } if (streamID == AHL_UNDEFINED) { err = CloseAllClientStreams(pClientCtx,&req); if (err) { afb_req_fail(req, "Error closing streams", "Streams cannot close"); return; } } else { err = CloseStream(pClientCtx,streamID,&req); if (err) { afb_req_fail_f(req, "Error closing stream", "Specified stream cannot close stream_id -> %d",streamID); return; } } afb_req_success(req, NULL, "Stream close completed"); } static int SetStreamState(AHLClientCtxT * in_pClientCtx,struct afb_req * pReq, streamID_t streamID, char * pStreamStateStr, int iMuteValue) { StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { afb_req_fail_f(*pReq, "Stream not found", "Specified stream not found stream_id -> %d",streamID); return AHL_FAIL; } // Verify that this client can control the stream int iStreamAccessControl = CheckStreamAccessControl( in_pClientCtx, streamID ); if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) { afb_req_fail(*pReq, "Access control denied", "Set stream state not allowed in current client context"); return AHL_FAIL; } if (pStreamStateStr != NULL) { StreamStateT streamState = StreamStateToEnum(pStreamStateStr); #ifndef AHL_DISCONNECT_POLICY json_object *pPolicyStreamJ = NULL; int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); if (err == AHL_POLICY_UTIL_FAIL) { afb_req_fail(*pReq, "Audio policy violation", "Unable to get JSON object for Policy_SetStreamState"); return AHL_FAIL; } json_object *paramJ= json_object_new_int(streamState); json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ); int policyAllowed = Policy_SetStreamState(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(*pReq, "Audio policy violation", "Change stream state not allowed in current context"); return AHL_FAIL; } #else // Simulate that policy returns target state (accepted) pStreamInfo->streamState = streamState; #endif } #ifndef AHL_DISCONNECT_POLICY json_object *pPolicyStreamJ = NULL; int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); if (err == AHL_POLICY_UTIL_FAIL) { afb_req_fail((*pReq), "Audio policy violation", "Unable to get JSON object for Policy_SetStreamMute"); return AHL_FAIL; } json_object *paramJ= json_object_new_int(iMuteValue); json_object_object_add(pPolicyStreamJ, "mute_state", paramJ); int policyAllowed = Policy_SetStreamMute(pPolicyStreamJ); if (policyAllowed == AHL_POLICY_REJECT) { afb_req_fail(*pReq, "Audio policy violation", "Mute stream not allowed in current context"); return AHL_FAIL; } #else // Simulate that policy returns target state (accepted) pStreamInfo->streamMute = (StreamMuteT)(*piMuteValue); #endif return AHL_SUCCESS; } PUBLIC void audiohlapi_set_stream_state(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; char * streamStateStr = NULL; int iMuteValue = 0; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s?i,s?s,s?i}", "stream_id", &streamID,"state",&streamStateStr,"mute",&iMuteValue); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } // Check if there is already an existing context for this client AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure if (pClientCtx == NULL) { afb_req_fail(req, "Bad state", "No client context associated with the request (is there an opened stream by this client?)"); return; } if (streamID == AHL_UNDEFINED) { // All stream for this client if (pClientCtx->pStreamAccessList != NULL) { for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++) { streamID_t curStreamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); err = SetStreamState(pClientCtx,&req,curStreamID,streamStateStr,iMuteValue); if (err) { return; } } } } else { err = SetStreamState(pClientCtx,&req,streamID,streamStateStr,iMuteValue); if (err) { return; } } afb_req_success(req, NULL, "Set stream state"); } PUBLIC void audiohlapi_get_stream_info(struct afb_req req) { json_object *queryJ = NULL; streamID_t streamID = AHL_UNDEFINED; json_object * streamInfoJ = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); return; } JSONPublicPackageStream(pStreamInfo,&streamInfoJ); afb_req_success(req, streamInfoJ, "Get stream info completed"); } PUBLIC void audiohlapi_volume(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * volumeStr = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?s}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"volume",&volumeStr); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) { afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); return; } if (volumeStr != NULL) { #ifndef AHL_DISCONNECT_POLICY json_object *pPolicyEndpointJ = NULL; err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); if (err == AHL_POLICY_UTIL_FAIL) { afb_req_fail(req, "Audio policy violation", "Unable to get JSON object for Policy_SetVolume"); return; } json_object *paramJ= json_object_new_string(volumeStr); json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); json_object * pPolicyVolumeReply = NULL; int policyAllowed = Policy_SetVolume(pPolicyEndpointJ,&pPolicyVolumeReply); if (!policyAllowed) { afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context"); return; } err = wrap_json_unpack(pPolicyVolumeReply,"{s:i}","volume",&pEndpointInfo->iVolume); if (err) { afb_req_fail_f(req, "Invalid policy reply", "Policy volume change reply not a valid json object=%s", json_object_get_string(pPolicyVolumeReply)); return; } #else // Simulate that policy returns target state (accepted) pEndpointInfo->iVolume = atoi(volumeStr); #endif } json_object * volumeJ = json_object_new_int(pEndpointInfo->iVolume); afb_req_success(req, volumeJ, "Set/get volume completed"); } PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) { afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); return; } json_object *endpointInfoJ = NULL; EndpointInfoToJSON(pEndpointInfo,&endpointInfoJ); afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties"); } PUBLIC void audiohlapi_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = AHL_UNDEFINED; char * pEndpointTypeStr = NULL; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * propertyName = NULL; json_object * propValueJ = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s,s?o}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } endpointType = EndpointTypeToEnum(pEndpointTypeStr); EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); if (pEndpointInfo == NULL) { afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); return; } if (propValueJ != NULL) { #ifndef AHL_DISCONNECT_POLICY json_object *pPolicyEndpointJ = NULL; err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); if (err == AHL_POLICY_UTIL_FAIL) { afb_req_fail(req, "property fail", "Unable to get JSON object for Policy_SetProperty"); return; } json_object *paramJ= json_object_new_string(propertyName); json_object_object_add(pPolicyEndpointJ, "arg_property_name", paramJ); json_object_object_add(pPolicyEndpointJ, "arg_property_value", propValueJ); // Call policy to allow custom policy actions in current context json_object * pPropertyReply = NULL; int policyAllowed = Policy_SetProperty(pPolicyEndpointJ,&pPropertyReply); if (!policyAllowed) { afb_req_fail(req, "Policy Reject Set Property Control", "Policy Reject Set Property Control"); return; } json_object * pPropReplyValue = NULL; err = wrap_json_unpack(pPropertyReply,"{s:o}","value",&pPropReplyValue); if (err) { afb_req_fail_f(req, "property fail", "Policy Property value not a valid json object=%s", json_object_get_string(pPropertyReply)); return; } if (pEndpointInfo->pPropTable && pPropReplyValue) { json_object_get(pPropReplyValue); g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, pPropReplyValue); } #else // Simulate that policy returns target state (accepted) if (pEndpointInfo->pPropTable && propValueJ) json_object_get(propValueJ); g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, propValueJ); #endif } // Retrieve cached property value json_object * propertyValJ = (json_object *)g_hash_table_lookup(pEndpointInfo->pPropTable,propertyName); if (propertyValJ == NULL) { afb_req_fail_f(req, "audiohlapi_property", "Property information not found: %s",propertyName); return; } json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object afb_req_success(req, propertyValJ, "Set/get property completed"); } PUBLIC void audiohlapi_get_list_actions(struct afb_req req) { json_object *queryJ = NULL; char * audioRole = NULL; json_object * roleActionsJ = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } // Build and return list of actions for specific audio role RoleInfoT * pRole = GetRole(audioRole); if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } roleActionsJ = json_object_new_array(); if (pRole->pActionList) { int iNumberActions = pRole->pActionList->len; for ( int i = 0 ; i < iNumberActions; i++) { char * pActionName = g_ptr_array_index(pRole->pActionList,i); json_object * actionJ = json_object_new_string(pActionName); json_object_array_add(roleActionsJ, actionJ); } } afb_req_success(req, roleActionsJ, "Retrieved action list for audio role"); } PUBLIC void audiohlapi_post_action(struct afb_req req) { json_object *queryJ = NULL; char * actionName = NULL; char * audioRole = NULL; char * mediaName = NULL; json_object *actionContext = NULL; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } // Verify if known action for audio role RoleInfoT * pRole = GetRole(audioRole); if ( pRole == NULL ) { afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); return; } // Check to find specific action int iActionFound = 0; if (pRole->pActionList) { int iNumberActions = pRole->pActionList->len; char * pTargetActionName = NULL; for ( int i = 0 ; i < iNumberActions; i++) { pTargetActionName = g_ptr_array_index(pRole->pActionList,i); if ( strcasecmp(pTargetActionName,actionName)==0) { iActionFound = 1; break; } } } if (!iActionFound) { afb_req_fail_f(req, "Event not found for audio role", "Event -> %s not found for role:%s",actionName,audioRole); return; } #ifndef AHL_DISCONNECT_POLICY // Call policy to allow custom policy actions in current context (e.g. cancel playback) json_object * pActionInfo = NULL; err = wrap_json_pack(&pActionInfo, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); if (err) { afb_req_fail_f(req, "Invalid arguments", "Could not create action JSON object arguments"); return; } json_object_get(pActionInfo); int policyAllowed = Policy_PostAction(pActionInfo); if (!policyAllowed) { afb_req_fail(req, "Audio policy violation", "Post sound action not allowed in current context"); return; } #endif afb_req_success(req, NULL, "Posted sound action"); } PUBLIC void audiohlapi_event_subscription(struct afb_req req) { json_object *queryJ = NULL; json_object * eventArrayJ = NULL; int iSubscribe = 1; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:o,s:i}", "events", &eventArrayJ,"subscribe",&iSubscribe); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } json_type jType = json_object_get_type(eventArrayJ); int iNumEvents = 0; if(jType == json_type_array) { iNumEvents = json_object_array_length(eventArrayJ); } for (int i = 0; i < iNumEvents; i++) { char * pEventName = NULL; json_object * jEvent = json_object_array_get_idx(eventArrayJ,i); pEventName = (char *)json_object_get_string(jEvent); if(pEventName == NULL) { afb_req_fail(req, "failed", "Invalid event"); return; } else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) { if (iSubscribe) afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent); else afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent); } else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) { if (iSubscribe) afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent); else afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent); } else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) { if (iSubscribe) afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); else afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent); } else { afb_req_fail(req, "failed", "Invalid event"); return; } } afb_req_success(req, NULL, "Event subscription update finished"); } // Since the policy is currently in the same binding, it cannot raise events on its own // This is a first step toward isolation, when policy is migrated in its own binding it can simply raise AGL events // This binding will register for these policy events and will execute the code below upon event reception PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) { char * pEventName = NULL; int err = wrap_json_unpack(pEventDataJ,"{s:s}","event_name", &pEventName); if(err) { AFB_ERROR("Unable to retrieve event name"); return; } if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) { char * pAudioRole = NULL; char * pPropertyName = NULL; endpointID_t endpointID = AHL_UNDEFINED; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; json_object * propValueJ = NULL; int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s,s:o,s:s}", "endpoint_id", &endpointID, "endpoint_type", &endpointType, "property_name", &pPropertyName, "value",&propValueJ, "audio_role", &pAudioRole); if(err) { AFB_ERROR("Unable to unpack property event"); return; } RoleInfoT * pRole = GetRole(pAudioRole); if ( pRole == NULL ){ AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); return; } EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); // update property value if ((pEndpointInfo!=NULL) && (pEndpointInfo->pPropTable!=NULL)) { json_type jType = json_object_get_type(propValueJ); switch (jType) { case json_type_double: g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_double(json_object_get_double(propValueJ))); break; case json_type_int: g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_int(json_object_get_int(propValueJ))); break; case json_type_string: g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_string(json_object_get_string(propValueJ))); break; default: AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(propValueJ)); return ; } } // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); } else if(strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)==0) { char * pAudioRole = NULL; endpointID_t endpointID = AHL_UNDEFINED; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; int iVolume = 0; int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:i,s:s}", "endpoint_id", &endpointID, "endpoint_type", &endpointType, "value",&iVolume, "audio_role", &pAudioRole); if(err) { AFB_ERROR("Unable to unpack volume event data"); return; } RoleInfoT * pRole = GetRole(pAudioRole); if ( pRole == NULL ){ AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); return; } EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); // update volume value if(pEndpointInfo) { pEndpointInfo->iVolume = iVolume; } else { AFB_ERROR("Unable to find endpoint"); } // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ); } else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) { // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); // BUG: This crashes... afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ); } else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) { streamID_t streamID = AHL_UNDEFINED; StreamEventT streamEvent = STREAM_EVENT_MAXVALUE; int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i}", "stream_id", &streamID, "state_event", &streamEvent); if(err) { AFB_ERROR("Unable to unpack stream event data"); return; } StreamInfoT * pStreamInfo = GetStream(streamID); if (pStreamInfo == NULL) { AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); return; } // update streamstate value switch (streamEvent) { case STREAM_EVENT_START: pStreamInfo->streamState = STREAM_STATE_RUNNING; break; case STREAM_EVENT_STOP: pStreamInfo->streamState = STREAM_STATE_IDLE; break; case STREAM_EVENT_PAUSE: pStreamInfo->streamState = STREAM_STATE_PAUSED; break; case STREAM_EVENT_RESUME: pStreamInfo->streamState = STREAM_STATE_RUNNING; break; case STREAM_EVENT_MUTED: pStreamInfo->streamMute = STREAM_MUTED; break; case STREAM_EVENT_UNMUTED: pStreamInfo->streamMute = STREAM_UNMUTED; break; default: AFB_ERROR("Unknown stream event"); } // Remove event name from object json_object_object_del(pEventDataJ,"event_name"); afb_event_push(pStreamInfo->streamStateEvent,pEventDataJ); } else { AFB_ERROR("Unknown event name"); } }