1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* DynamicInterfaceManagement implementation */
18
19#include "sles_allinclusive.h"
20
21
22// Called by a worker thread to handle an asynchronous AddInterface.
23// Parameter self is the DynamicInterface, and MPH specifies which interface to add.
24
25static void HandleAdd(void *self, void *ignored, int MPH)
26{
27
28    // validate input parameters
29    IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
30    assert(NULL != thiz);
31    IObject *thisObject = InterfaceToIObject(thiz);
32    assert(NULL != thisObject);
33    assert(0 <= MPH && MPH < MPH_MAX);
34    const ClassTable *clazz = thisObject->mClass;
35    assert(NULL != clazz);
36    int index = clazz->mMPH_to_index[MPH];
37    assert(0 <= index && index < (int) clazz->mInterfaceCount);
38    SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
39    SLresult result;
40
41    // check interface state
42    object_lock_exclusive(thisObject);
43    SLuint8 state = *interfaceStateP;
44    switch (state) {
45
46    case INTERFACE_ADDING_1:    // normal case
47        {
48        // change state to indicate we are now adding the interface
49        *interfaceStateP = INTERFACE_ADDING_2;
50        object_unlock_exclusive(thisObject);
51
52        // this section runs with mutex unlocked
53        const struct iid_vtable *x = &clazz->mInterfaces[index];
54        size_t offset = x->mOffset;
55        void *thisItf = (char *) thisObject + offset;
56        BoolHook expose = MPH_init_table[MPH].mExpose;
57        // call the optional expose hook
58        if ((NULL == expose) || (*expose)(thisItf)) {
59            result = SL_RESULT_SUCCESS;
60        } else {
61            result = SL_RESULT_FEATURE_UNSUPPORTED;
62        }
63
64        // re-lock mutex to update state
65        object_lock_exclusive(thisObject);
66        assert(INTERFACE_ADDING_2 == *interfaceStateP);
67        if (SL_RESULT_SUCCESS == result) {
68            ((size_t *) thisItf)[0] ^= ~0;
69            state = INTERFACE_ADDED;
70        } else {
71            state = INTERFACE_INITIALIZED;
72        }
73        }
74        break;
75
76    case INTERFACE_ADDING_1A:   // operation was aborted while on work queue
77        result = SL_RESULT_OPERATION_ABORTED;
78        state = INTERFACE_INITIALIZED;
79        break;
80
81    default:                    // impossible
82        assert(SL_BOOLEAN_FALSE);
83        result = SL_RESULT_INTERNAL_ERROR;
84        break;
85
86    }
87
88    // mutex is locked, update state
89    *interfaceStateP = state;
90
91    // Make a copy of these, so we can call the callback with mutex unlocked
92    slDynamicInterfaceManagementCallback callback = thiz->mCallback;
93    void *context = thiz->mContext;
94    object_unlock_exclusive(thisObject);
95
96    // Note that the mutex is unlocked during the callback
97    if (NULL != callback) {
98        const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID
99        (*callback)(&thiz->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
100    }
101
102}
103
104
105static SLresult IDynamicInterfaceManagement_AddInterface(SLDynamicInterfaceManagementItf self,
106    const SLInterfaceID iid, SLboolean async)
107{
108    SL_ENTER_INTERFACE
109
110    // validate input parameters
111    if (NULL == iid) {
112        result = SL_RESULT_PARAMETER_INVALID;
113    } else {
114        IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
115        IObject *thisObject = InterfaceToIObject(thiz);
116        const ClassTable *clazz = thisObject->mClass;
117        int MPH, index;
118        if ((0 > (MPH = IID_to_MPH(iid))) ||
119                // no need to check for an initialization hook
120                // (NULL == MPH_init_table[MPH].mInit) ||
121                (0 > (index = clazz->mMPH_to_index[MPH]))) {
122            result = SL_RESULT_FEATURE_UNSUPPORTED;
123        } else {
124            assert(index < (int) clazz->mInterfaceCount);
125            SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
126
127            // check interface state
128            object_lock_exclusive(thisObject);
129            switch (*interfaceStateP) {
130
131            case INTERFACE_INITIALIZED: // normal case
132                if (async) {
133                    // Asynchronous: mark operation pending and cancellable
134                    *interfaceStateP = INTERFACE_ADDING_1;
135                    object_unlock_exclusive(thisObject);
136
137                    // this section runs with mutex unlocked
138                    result = ThreadPool_add_ppi(&thisObject->mEngine->mThreadPool, HandleAdd, thiz,
139                        NULL, MPH);
140                    if (SL_RESULT_SUCCESS != result) {
141                        // Engine was destroyed during add, or insufficient memory,
142                        // so restore mInterfaceStates state to prior value
143                        object_lock_exclusive(thisObject);
144                        switch (*interfaceStateP) {
145                        case INTERFACE_ADDING_1:    // normal
146                        case INTERFACE_ADDING_1A:   // operation aborted while mutex unlocked
147                            *interfaceStateP = INTERFACE_INITIALIZED;
148                            break;
149                        default:                    // unexpected
150                            // leave state alone
151                            break;
152                        }
153                    }
154
155                } else {
156                    // Synchronous: mark operation pending to prevent duplication
157                    *interfaceStateP = INTERFACE_ADDING_2;
158                    object_unlock_exclusive(thisObject);
159
160                    // this section runs with mutex unlocked
161                    const struct iid_vtable *x = &clazz->mInterfaces[index];
162                    size_t offset = x->mOffset;
163                    void *thisItf = (char *) thisObject + offset;
164                    // call the optional expose hook
165                    BoolHook expose = MPH_init_table[MPH].mExpose;
166                    if ((NULL == expose) || (*expose)(thisItf)) {
167                        result = SL_RESULT_SUCCESS;
168                    } else {
169                        result = SL_RESULT_FEATURE_UNSUPPORTED;
170                    }
171
172                    // re-lock mutex to update state
173                    object_lock_exclusive(thisObject);
174                    assert(INTERFACE_ADDING_2 == *interfaceStateP);
175                    if (SL_RESULT_SUCCESS == result) {
176                        *interfaceStateP = INTERFACE_ADDED;
177                    } else {
178                        *interfaceStateP = INTERFACE_INITIALIZED;
179                    }
180                }
181
182                // mutex is still locked
183                break;
184
185            default:    // disallow adding of (partially) initialized interfaces
186                result = SL_RESULT_PRECONDITIONS_VIOLATED;
187                break;
188
189            }
190
191            object_unlock_exclusive(thisObject);
192
193        }
194    }
195
196    SL_LEAVE_INTERFACE
197}
198
199
200static SLresult IDynamicInterfaceManagement_RemoveInterface(
201    SLDynamicInterfaceManagementItf self, const SLInterfaceID iid)
202{
203    SL_ENTER_INTERFACE
204
205#if USE_PROFILES & USE_PROFILES_BASE
206    // validate input parameters
207    if (NULL == iid) {
208        result = SL_RESULT_PARAMETER_INVALID;
209    } else {
210        IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
211        IObject *thisObject = InterfaceToIObject(thiz);
212        const ClassTable *clazz = thisObject->mClass;
213        int MPH, index;
214        if ((0 > (MPH = IID_to_MPH(iid))) ||
215                // no need to check for an initialization hook
216                // (NULL == MPH_init_table[MPH].mInit) ||
217                (0 > (index = clazz->mMPH_to_index[MPH]))) {
218            result = SL_RESULT_PRECONDITIONS_VIOLATED;
219        } else {
220            SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
221
222            // check interface state
223            object_lock_exclusive(thisObject);
224            switch (*interfaceStateP) {
225
226            case INTERFACE_ADDED:       // normal cases
227            case INTERFACE_SUSPENDED:
228                {
229                // Compute address of the interface
230                const struct iid_vtable *x = &clazz->mInterfaces[index];
231                size_t offset = x->mOffset;
232                void *thisItf = (char *) thisObject + offset;
233
234                // Mark operation pending (not necessary; remove is synchronous with mutex locked)
235                *interfaceStateP = INTERFACE_REMOVING;
236
237                // Check if application ever called Object::GetInterface
238                unsigned mask = 1 << index;
239                if (thisObject->mGottenMask & mask) {
240                    thisObject->mGottenMask &= ~mask;
241                    // This trickery invalidates the v-table
242                    ((size_t *) thisItf)[0] ^= ~0;
243                }
244
245                // The remove hook is called with mutex locked
246                VoidHook remove = MPH_init_table[MPH].mRemove;
247                if (NULL != remove) {
248                    (*remove)(thisItf);
249                }
250                result = SL_RESULT_SUCCESS;
251
252                assert(INTERFACE_REMOVING == *interfaceStateP);
253                *interfaceStateP = INTERFACE_INITIALIZED;
254                }
255
256                // mutex is still locked
257                break;
258
259            default:
260                // disallow removal of non-dynamic interfaces, or interfaces which are
261                // currently being resumed (will not auto-cancel an asynchronous resume)
262                result = SL_RESULT_PRECONDITIONS_VIOLATED;
263                break;
264
265            }
266
267            object_unlock_exclusive(thisObject);
268        }
269    }
270#else
271    result = SL_RESULT_FEATURE_UNSUPPORTED;
272#endif
273
274    SL_LEAVE_INTERFACE
275}
276
277
278// Called by a worker thread to handle an asynchronous ResumeInterface.
279// Parameter self is the DynamicInterface, and MPH specifies which interface to resume.
280
281static void HandleResume(void *self, void *ignored, int MPH)
282{
283
284    // validate input parameters
285    IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
286    assert(NULL != thiz);
287    IObject *thisObject = InterfaceToIObject(thiz);
288    assert(NULL != thisObject);
289    assert(0 <= MPH && MPH < MPH_MAX);
290    const ClassTable *clazz = thisObject->mClass;
291    assert(NULL != clazz);
292    int index = clazz->mMPH_to_index[MPH];
293    assert(0 <= index && index < (int) clazz->mInterfaceCount);
294    SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
295    SLresult result;
296
297    // check interface state
298    object_lock_exclusive(thisObject);
299    SLuint8 state = *interfaceStateP;
300    switch (state) {
301
302    case INTERFACE_RESUMING_1:      // normal case
303        {
304        // change state to indicate we are now resuming the interface
305        *interfaceStateP = INTERFACE_RESUMING_2;
306        object_unlock_exclusive(thisObject);
307
308        // this section runs with mutex unlocked
309        const struct iid_vtable *x = &clazz->mInterfaces[index];
310        size_t offset = x->mOffset;
311        void *thisItf = (char *) thisObject + offset;
312        VoidHook resume = MPH_init_table[MPH].mResume;
313        if (NULL != resume) {
314            (*resume)(thisItf);
315        }
316        result = SL_RESULT_SUCCESS;
317
318        // re-lock mutex to update state
319        object_lock_exclusive(thisObject);
320        assert(INTERFACE_RESUMING_2 == *interfaceStateP);
321        state = INTERFACE_ADDED;
322        }
323        break;
324
325    case INTERFACE_RESUMING_1A:     // operation was aborted while on work queue
326        result = SL_RESULT_OPERATION_ABORTED;
327        state = INTERFACE_SUSPENDED;
328        break;
329
330    default:                        // impossible
331        assert(SL_BOOLEAN_FALSE);
332        result = SL_RESULT_INTERNAL_ERROR;
333        break;
334
335    }
336
337    // mutex is locked, update state
338    *interfaceStateP = state;
339
340    // Make a copy of these, so we can call the callback with mutex unlocked
341    slDynamicInterfaceManagementCallback callback = thiz->mCallback;
342    void *context = thiz->mContext;
343    object_unlock_exclusive(thisObject);
344
345    // Note that the mutex is unlocked during the callback
346    if (NULL != callback) {
347        const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID
348        (*callback)(&thiz->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid);
349    }
350}
351
352
353static SLresult IDynamicInterfaceManagement_ResumeInterface(SLDynamicInterfaceManagementItf self,
354    const SLInterfaceID iid, SLboolean async)
355{
356    SL_ENTER_INTERFACE
357
358    // validate input parameters
359    if (NULL == iid) {
360        result = SL_RESULT_PARAMETER_INVALID;
361    } else {
362        IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
363        IObject *thisObject = InterfaceToIObject(thiz);
364        const ClassTable *clazz = thisObject->mClass;
365        int MPH, index;
366        if ((0 > (MPH = IID_to_MPH(iid))) ||
367                // no need to check for an initialization hook
368                // (NULL == MPH_init_table[MPH].mInit) ||
369                (0 > (index = clazz->mMPH_to_index[MPH]))) {
370            result = SL_RESULT_PRECONDITIONS_VIOLATED;
371        } else {
372            assert(index < (int) clazz->mInterfaceCount);
373            SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index];
374
375            // check interface state
376            object_lock_exclusive(thisObject);
377            switch (*interfaceStateP) {
378
379            case INTERFACE_SUSPENDED:   // normal case
380                if (async) {
381                    // Asynchronous: mark operation pending and cancellable
382                    *interfaceStateP = INTERFACE_RESUMING_1;
383                    object_unlock_exclusive(thisObject);
384
385                    // this section runs with mutex unlocked
386                    result = ThreadPool_add_ppi(&thisObject->mEngine->mThreadPool, HandleResume,
387                        thiz, NULL, MPH);
388                    if (SL_RESULT_SUCCESS != result) {
389                        // Engine was destroyed during resume, or insufficient memory,
390                        // so restore mInterfaceStates state to prior value
391                        object_lock_exclusive(thisObject);
392                        switch (*interfaceStateP) {
393                        case INTERFACE_RESUMING_1:  // normal
394                        case INTERFACE_RESUMING_1A: // operation aborted while mutex unlocked
395                            *interfaceStateP = INTERFACE_SUSPENDED;
396                            break;
397                        default:                    // unexpected
398                            // leave state alone
399                            break;
400                        }
401                    }
402
403                } else {
404                    // Synchronous: mark operation pending to prevent duplication
405                    *interfaceStateP = INTERFACE_RESUMING_2;
406                    object_unlock_exclusive(thisObject);
407
408                    // this section runs with mutex unlocked
409                    const struct iid_vtable *x = &clazz->mInterfaces[index];
410                    size_t offset = x->mOffset;
411                    void *thisItf = (char *) thiz + offset;
412                    VoidHook resume = MPH_init_table[MPH].mResume;
413                    if (NULL != resume) {
414                        (*resume)(thisItf);
415                    }
416                    result = SL_RESULT_SUCCESS;
417
418                    // re-lock mutex to update state
419                    object_lock_exclusive(thisObject);
420                    assert(INTERFACE_RESUMING_2 == *interfaceStateP);
421                    *interfaceStateP = INTERFACE_ADDED;
422                }
423
424                // mutex is now locked
425                break;
426
427            default:    // disallow resumption of non-suspended interfaces
428                result = SL_RESULT_PRECONDITIONS_VIOLATED;
429                break;
430            }
431
432            object_unlock_exclusive(thisObject);
433        }
434    }
435
436    SL_LEAVE_INTERFACE
437}
438
439
440static SLresult IDynamicInterfaceManagement_RegisterCallback(SLDynamicInterfaceManagementItf self,
441    slDynamicInterfaceManagementCallback callback, void *pContext)
442{
443    SL_ENTER_INTERFACE
444
445    IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
446    IObject *thisObject = InterfaceToIObject(thiz);
447    object_lock_exclusive(thisObject);
448    thiz->mCallback = callback;
449    thiz->mContext = pContext;
450    object_unlock_exclusive(thisObject);
451    result = SL_RESULT_SUCCESS;
452
453    SL_LEAVE_INTERFACE
454}
455
456
457static const struct SLDynamicInterfaceManagementItf_ IDynamicInterfaceManagement_Itf = {
458    IDynamicInterfaceManagement_AddInterface,
459    IDynamicInterfaceManagement_RemoveInterface,
460    IDynamicInterfaceManagement_ResumeInterface,
461    IDynamicInterfaceManagement_RegisterCallback
462};
463
464void IDynamicInterfaceManagement_init(void *self)
465{
466    IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self;
467    thiz->mItf = &IDynamicInterfaceManagement_Itf;
468    thiz->mCallback = NULL;
469    thiz->mContext = NULL;
470}
471