| /* | |
| * Copyright (C) 2010 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /* DynamicInterfaceManagement implementation */ | |
| #include "sles_allinclusive.h" | |
| // Called by a worker thread to handle an asynchronous AddInterface. | |
| // Parameter self is the DynamicInterface, and MPH specifies which interface to add. | |
| static void HandleAdd(void *self, void *ignored, int MPH) | |
| { | |
| // validate input parameters | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| assert(NULL != thiz); | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| assert(NULL != thisObject); | |
| assert(0 <= MPH && MPH < MPH_MAX); | |
| const ClassTable *clazz = thisObject->mClass; | |
| assert(NULL != clazz); | |
| int index = clazz->mMPH_to_index[MPH]; | |
| assert(0 <= index && index < (int) clazz->mInterfaceCount); | |
| SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; | |
| SLresult result; | |
| // check interface state | |
| object_lock_exclusive(thisObject); | |
| SLuint8 state = *interfaceStateP; | |
| switch (state) { | |
| case INTERFACE_ADDING_1: // normal case | |
| { | |
| // change state to indicate we are now adding the interface | |
| *interfaceStateP = INTERFACE_ADDING_2; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| const struct iid_vtable *x = &clazz->mInterfaces[index]; | |
| size_t offset = x->mOffset; | |
| void *thisItf = (char *) thisObject + offset; | |
| BoolHook expose = MPH_init_table[MPH].mExpose; | |
| // call the optional expose hook | |
| if ((NULL == expose) || (*expose)(thisItf)) { | |
| result = SL_RESULT_SUCCESS; | |
| } else { | |
| result = SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| // re-lock mutex to update state | |
| object_lock_exclusive(thisObject); | |
| assert(INTERFACE_ADDING_2 == *interfaceStateP); | |
| if (SL_RESULT_SUCCESS == result) { | |
| ((size_t *) thisItf)[0] ^= ~0; | |
| state = INTERFACE_ADDED; | |
| } else { | |
| state = INTERFACE_INITIALIZED; | |
| } | |
| } | |
| break; | |
| case INTERFACE_ADDING_1A: // operation was aborted while on work queue | |
| result = SL_RESULT_OPERATION_ABORTED; | |
| state = INTERFACE_INITIALIZED; | |
| break; | |
| default: // impossible | |
| assert(SL_BOOLEAN_FALSE); | |
| result = SL_RESULT_INTERNAL_ERROR; | |
| break; | |
| } | |
| // mutex is locked, update state | |
| *interfaceStateP = state; | |
| // Make a copy of these, so we can call the callback with mutex unlocked | |
| slDynamicInterfaceManagementCallback callback = thiz->mCallback; | |
| void *context = thiz->mContext; | |
| object_unlock_exclusive(thisObject); | |
| // Note that the mutex is unlocked during the callback | |
| if (NULL != callback) { | |
| const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID | |
| (*callback)(&thiz->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid); | |
| } | |
| } | |
| static SLresult IDynamicInterfaceManagement_AddInterface(SLDynamicInterfaceManagementItf self, | |
| const SLInterfaceID iid, SLboolean async) | |
| { | |
| SL_ENTER_INTERFACE | |
| // validate input parameters | |
| if (NULL == iid) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| const ClassTable *clazz = thisObject->mClass; | |
| int MPH, index; | |
| if ((0 > (MPH = IID_to_MPH(iid))) || | |
| // no need to check for an initialization hook | |
| // (NULL == MPH_init_table[MPH].mInit) || | |
| (0 > (index = clazz->mMPH_to_index[MPH]))) { | |
| result = SL_RESULT_FEATURE_UNSUPPORTED; | |
| } else { | |
| assert(index < (int) clazz->mInterfaceCount); | |
| SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; | |
| // check interface state | |
| object_lock_exclusive(thisObject); | |
| switch (*interfaceStateP) { | |
| case INTERFACE_INITIALIZED: // normal case | |
| if (async) { | |
| // Asynchronous: mark operation pending and cancellable | |
| *interfaceStateP = INTERFACE_ADDING_1; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| result = ThreadPool_add_ppi(&thisObject->mEngine->mThreadPool, HandleAdd, thiz, | |
| NULL, MPH); | |
| if (SL_RESULT_SUCCESS != result) { | |
| // Engine was destroyed during add, or insufficient memory, | |
| // so restore mInterfaceStates state to prior value | |
| object_lock_exclusive(thisObject); | |
| switch (*interfaceStateP) { | |
| case INTERFACE_ADDING_1: // normal | |
| case INTERFACE_ADDING_1A: // operation aborted while mutex unlocked | |
| *interfaceStateP = INTERFACE_INITIALIZED; | |
| break; | |
| default: // unexpected | |
| // leave state alone | |
| break; | |
| } | |
| } | |
| } else { | |
| // Synchronous: mark operation pending to prevent duplication | |
| *interfaceStateP = INTERFACE_ADDING_2; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| const struct iid_vtable *x = &clazz->mInterfaces[index]; | |
| size_t offset = x->mOffset; | |
| void *thisItf = (char *) thisObject + offset; | |
| // call the optional expose hook | |
| BoolHook expose = MPH_init_table[MPH].mExpose; | |
| if ((NULL == expose) || (*expose)(thisItf)) { | |
| result = SL_RESULT_SUCCESS; | |
| } else { | |
| result = SL_RESULT_FEATURE_UNSUPPORTED; | |
| } | |
| // re-lock mutex to update state | |
| object_lock_exclusive(thisObject); | |
| assert(INTERFACE_ADDING_2 == *interfaceStateP); | |
| if (SL_RESULT_SUCCESS == result) { | |
| *interfaceStateP = INTERFACE_ADDED; | |
| } else { | |
| *interfaceStateP = INTERFACE_INITIALIZED; | |
| } | |
| } | |
| // mutex is still locked | |
| break; | |
| default: // disallow adding of (partially) initialized interfaces | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| break; | |
| } | |
| object_unlock_exclusive(thisObject); | |
| } | |
| } | |
| SL_LEAVE_INTERFACE | |
| } | |
| static SLresult IDynamicInterfaceManagement_RemoveInterface( | |
| SLDynamicInterfaceManagementItf self, const SLInterfaceID iid) | |
| { | |
| SL_ENTER_INTERFACE | |
| #if USE_PROFILES & USE_PROFILES_BASE | |
| // validate input parameters | |
| if (NULL == iid) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| const ClassTable *clazz = thisObject->mClass; | |
| int MPH, index; | |
| if ((0 > (MPH = IID_to_MPH(iid))) || | |
| // no need to check for an initialization hook | |
| // (NULL == MPH_init_table[MPH].mInit) || | |
| (0 > (index = clazz->mMPH_to_index[MPH]))) { | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| } else { | |
| SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; | |
| // check interface state | |
| object_lock_exclusive(thisObject); | |
| switch (*interfaceStateP) { | |
| case INTERFACE_ADDED: // normal cases | |
| case INTERFACE_SUSPENDED: | |
| { | |
| // Compute address of the interface | |
| const struct iid_vtable *x = &clazz->mInterfaces[index]; | |
| size_t offset = x->mOffset; | |
| void *thisItf = (char *) thisObject + offset; | |
| // Mark operation pending (not necessary; remove is synchronous with mutex locked) | |
| *interfaceStateP = INTERFACE_REMOVING; | |
| // Check if application ever called Object::GetInterface | |
| unsigned mask = 1 << index; | |
| if (thisObject->mGottenMask & mask) { | |
| thisObject->mGottenMask &= ~mask; | |
| // This trickery invalidates the v-table | |
| ((size_t *) thisItf)[0] ^= ~0; | |
| } | |
| // The remove hook is called with mutex locked | |
| VoidHook remove = MPH_init_table[MPH].mRemove; | |
| if (NULL != remove) { | |
| (*remove)(thisItf); | |
| } | |
| result = SL_RESULT_SUCCESS; | |
| assert(INTERFACE_REMOVING == *interfaceStateP); | |
| *interfaceStateP = INTERFACE_INITIALIZED; | |
| } | |
| // mutex is still locked | |
| break; | |
| default: | |
| // disallow removal of non-dynamic interfaces, or interfaces which are | |
| // currently being resumed (will not auto-cancel an asynchronous resume) | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| break; | |
| } | |
| object_unlock_exclusive(thisObject); | |
| } | |
| } | |
| #else | |
| result = SL_RESULT_FEATURE_UNSUPPORTED; | |
| #endif | |
| SL_LEAVE_INTERFACE | |
| } | |
| // Called by a worker thread to handle an asynchronous ResumeInterface. | |
| // Parameter self is the DynamicInterface, and MPH specifies which interface to resume. | |
| static void HandleResume(void *self, void *ignored, int MPH) | |
| { | |
| // validate input parameters | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| assert(NULL != thiz); | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| assert(NULL != thisObject); | |
| assert(0 <= MPH && MPH < MPH_MAX); | |
| const ClassTable *clazz = thisObject->mClass; | |
| assert(NULL != clazz); | |
| int index = clazz->mMPH_to_index[MPH]; | |
| assert(0 <= index && index < (int) clazz->mInterfaceCount); | |
| SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; | |
| SLresult result; | |
| // check interface state | |
| object_lock_exclusive(thisObject); | |
| SLuint8 state = *interfaceStateP; | |
| switch (state) { | |
| case INTERFACE_RESUMING_1: // normal case | |
| { | |
| // change state to indicate we are now resuming the interface | |
| *interfaceStateP = INTERFACE_RESUMING_2; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| const struct iid_vtable *x = &clazz->mInterfaces[index]; | |
| size_t offset = x->mOffset; | |
| void *thisItf = (char *) thisObject + offset; | |
| VoidHook resume = MPH_init_table[MPH].mResume; | |
| if (NULL != resume) { | |
| (*resume)(thisItf); | |
| } | |
| result = SL_RESULT_SUCCESS; | |
| // re-lock mutex to update state | |
| object_lock_exclusive(thisObject); | |
| assert(INTERFACE_RESUMING_2 == *interfaceStateP); | |
| state = INTERFACE_ADDED; | |
| } | |
| break; | |
| case INTERFACE_RESUMING_1A: // operation was aborted while on work queue | |
| result = SL_RESULT_OPERATION_ABORTED; | |
| state = INTERFACE_SUSPENDED; | |
| break; | |
| default: // impossible | |
| assert(SL_BOOLEAN_FALSE); | |
| result = SL_RESULT_INTERNAL_ERROR; | |
| break; | |
| } | |
| // mutex is locked, update state | |
| *interfaceStateP = state; | |
| // Make a copy of these, so we can call the callback with mutex unlocked | |
| slDynamicInterfaceManagementCallback callback = thiz->mCallback; | |
| void *context = thiz->mContext; | |
| object_unlock_exclusive(thisObject); | |
| // Note that the mutex is unlocked during the callback | |
| if (NULL != callback) { | |
| const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID | |
| (*callback)(&thiz->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid); | |
| } | |
| } | |
| static SLresult IDynamicInterfaceManagement_ResumeInterface(SLDynamicInterfaceManagementItf self, | |
| const SLInterfaceID iid, SLboolean async) | |
| { | |
| SL_ENTER_INTERFACE | |
| // validate input parameters | |
| if (NULL == iid) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| const ClassTable *clazz = thisObject->mClass; | |
| int MPH, index; | |
| if ((0 > (MPH = IID_to_MPH(iid))) || | |
| // no need to check for an initialization hook | |
| // (NULL == MPH_init_table[MPH].mInit) || | |
| (0 > (index = clazz->mMPH_to_index[MPH]))) { | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| } else { | |
| assert(index < (int) clazz->mInterfaceCount); | |
| SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; | |
| // check interface state | |
| object_lock_exclusive(thisObject); | |
| switch (*interfaceStateP) { | |
| case INTERFACE_SUSPENDED: // normal case | |
| if (async) { | |
| // Asynchronous: mark operation pending and cancellable | |
| *interfaceStateP = INTERFACE_RESUMING_1; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| result = ThreadPool_add_ppi(&thisObject->mEngine->mThreadPool, HandleResume, | |
| thiz, NULL, MPH); | |
| if (SL_RESULT_SUCCESS != result) { | |
| // Engine was destroyed during resume, or insufficient memory, | |
| // so restore mInterfaceStates state to prior value | |
| object_lock_exclusive(thisObject); | |
| switch (*interfaceStateP) { | |
| case INTERFACE_RESUMING_1: // normal | |
| case INTERFACE_RESUMING_1A: // operation aborted while mutex unlocked | |
| *interfaceStateP = INTERFACE_SUSPENDED; | |
| break; | |
| default: // unexpected | |
| // leave state alone | |
| break; | |
| } | |
| } | |
| } else { | |
| // Synchronous: mark operation pending to prevent duplication | |
| *interfaceStateP = INTERFACE_RESUMING_2; | |
| object_unlock_exclusive(thisObject); | |
| // this section runs with mutex unlocked | |
| const struct iid_vtable *x = &clazz->mInterfaces[index]; | |
| size_t offset = x->mOffset; | |
| void *thisItf = (char *) thiz + offset; | |
| VoidHook resume = MPH_init_table[MPH].mResume; | |
| if (NULL != resume) { | |
| (*resume)(thisItf); | |
| } | |
| result = SL_RESULT_SUCCESS; | |
| // re-lock mutex to update state | |
| object_lock_exclusive(thisObject); | |
| assert(INTERFACE_RESUMING_2 == *interfaceStateP); | |
| *interfaceStateP = INTERFACE_ADDED; | |
| } | |
| // mutex is now locked | |
| break; | |
| default: // disallow resumption of non-suspended interfaces | |
| result = SL_RESULT_PRECONDITIONS_VIOLATED; | |
| break; | |
| } | |
| object_unlock_exclusive(thisObject); | |
| } | |
| } | |
| SL_LEAVE_INTERFACE | |
| } | |
| static SLresult IDynamicInterfaceManagement_RegisterCallback(SLDynamicInterfaceManagementItf self, | |
| slDynamicInterfaceManagementCallback callback, void *pContext) | |
| { | |
| SL_ENTER_INTERFACE | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| IObject *thisObject = InterfaceToIObject(thiz); | |
| object_lock_exclusive(thisObject); | |
| thiz->mCallback = callback; | |
| thiz->mContext = pContext; | |
| object_unlock_exclusive(thisObject); | |
| result = SL_RESULT_SUCCESS; | |
| SL_LEAVE_INTERFACE | |
| } | |
| static const struct SLDynamicInterfaceManagementItf_ IDynamicInterfaceManagement_Itf = { | |
| IDynamicInterfaceManagement_AddInterface, | |
| IDynamicInterfaceManagement_RemoveInterface, | |
| IDynamicInterfaceManagement_ResumeInterface, | |
| IDynamicInterfaceManagement_RegisterCallback | |
| }; | |
| void IDynamicInterfaceManagement_init(void *self) | |
| { | |
| IDynamicInterfaceManagement *thiz = (IDynamicInterfaceManagement *) self; | |
| thiz->mItf = &IDynamicInterfaceManagement_Itf; | |
| thiz->mCallback = NULL; | |
| thiz->mContext = NULL; | |
| } |