| /* |
| * 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; |
| } |