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 
HandleAdd(void * self,void * ignored,int MPH)25 static 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 
IDynamicInterfaceManagement_AddInterface(SLDynamicInterfaceManagementItf self,const SLInterfaceID iid,SLboolean async)105 static 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 
IDynamicInterfaceManagement_RemoveInterface(SLDynamicInterfaceManagementItf self,const SLInterfaceID iid)200 static 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 
HandleResume(void * self,void * ignored,int MPH)281 static 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 
IDynamicInterfaceManagement_ResumeInterface(SLDynamicInterfaceManagementItf self,const SLInterfaceID iid,SLboolean async)353 static 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 
IDynamicInterfaceManagement_RegisterCallback(SLDynamicInterfaceManagementItf self,slDynamicInterfaceManagementCallback callback,void * pContext)440 static 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 
457 static const struct SLDynamicInterfaceManagementItf_ IDynamicInterfaceManagement_Itf = {
458     IDynamicInterfaceManagement_AddInterface,
459     IDynamicInterfaceManagement_RemoveInterface,
460     IDynamicInterfaceManagement_ResumeInterface,
461     IDynamicInterfaceManagement_RegisterCallback
462 };
463 
IDynamicInterfaceManagement_init(void * self)464 void 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