// Update the subscriptions we maintain to agree with the current contact state void AppearanceGroup::updateSubscriptions() { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions mUri = '%s'", mSharedUser.data()); // First, scan mSubscriptions to construct a list of all the // call-id/contact combinations. // (If a phone reboots and starts registering with a different Call-Id, // the call-id/contact combination will be different even if the contact // URI is unchanged. So the new registration will appear to be different // to this machinery, and it will establish a new Appearance to // the contact URI. This compensates for the fact that the previous // Appearance to the contact URI appears to the Appearance Agent to be // working but the phone no longer knows of the subscription. The // reg events will eventually terminate the old combination and we // will delete its Appearance.) UtlHashBag callid_contacts; UtlHashMapIterator subs_itor(mSubscriptions); while (subs_itor()) { if (Os::Logger::instance().willLog(FAC_SAA, PRI_DEBUG)) { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions subscription '%s'", (dynamic_cast <UtlString*> (subs_itor.key()))->data()); } UtlHashMap* contact_state = dynamic_cast <UtlHashMap*> (subs_itor.value()); Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions contact_state = %p", contact_state); UtlHashMapIterator contact_itor(*contact_state); while (contact_itor()) { UtlString* contact = dynamic_cast <UtlString*> (contact_itor.value()); if (Os::Logger::instance().willLog(FAC_SAA, PRI_DEBUG)) { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions contact id '%s', Call-Id/URI '%s'", (dynamic_cast <UtlString*> (contact_itor.key()))->data(), contact->data()); } // Check if the contact is already in callid_contacts. if (!callid_contacts.find(contact)) { // If not, add it. UtlString* c = new UtlString(*contact); callid_contacts.insert(c); Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions contact '%s' added", c->data()); } } } // Now that we have a clean list of callid_contacts, update // Appearances to match it. // If we both terminate subscriptions and create subscriptions, // wait a short while to allow the terminations to complete. This // should not be necessary, but it makes life easier on Polycom // phones which (at this time) cannot support two subscriptions at // a time, and if the termination of the old subscription arrives // after the initiation of the new subscription, the new // subscription will be lost. // This variable tracks whether such a wait is needed before a // subscription is started. bool subscription_ended_but_no_wait_done_yet = false; // Iterate through the list of Appearances and remove any that aren't // in callid_contacts. { UtlHashMapIterator itor(mAppearances); UtlString* ss; while ((ss = dynamic_cast <UtlString*> (itor()))) { if (!callid_contacts.find(ss)) { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions deleting subscription for '%s' in mUri = '%s'", ss->data(), mSharedUser.data()); // Terminate all dialogs for this Appearance, then publish - before we delete it bool bContentChanged = false; SipDialogEvent* lPartialContent = new SipDialogEvent("partial", mSharedUser.data()); Appearance* inst = dynamic_cast <Appearance*> (itor.value()); bContentChanged = inst->terminateDialogs(true); // terminate all dialogs inst->getDialogs(lPartialContent); if (bContentChanged) { lPartialContent->buildBody(); publish(true, true, lPartialContent); delete lPartialContent; } mAppearances.destroy(ss); subscription_ended_but_no_wait_done_yet = true; } else { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions found subscription for '%s' in mUri = '%s'", ss->data(), mSharedUser.data()); } } } // Iterate through callid_contacts and add an Appearance for // any that aren't in mAppearances. // We don't limit the number of additions here, as the size of // callid_contacts is guarded by the tests in notifyEventCallback. { UtlHashBagIterator itor(callid_contacts); UtlString* callid_contact; while ((callid_contact = dynamic_cast <UtlString*> (itor()))) { if (!mAppearances.find(callid_contact)) { // If we both terminate subscriptions and create subscriptions, // wait a short while to allow the terminations to complete. if (SUBSCRIPTION_WAIT > 0) { if (subscription_ended_but_no_wait_done_yet) { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions waiting for %d msec", SUBSCRIPTION_WAIT); OsTask::delay(SUBSCRIPTION_WAIT); subscription_ended_but_no_wait_done_yet = false; } } Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions adding subscription for '%s' in mUri = '%s'", callid_contact->data(), mSharedUser.data()); // Get the contact URI into a UtlString. UtlString uri(callid_contact->data() + callid_contact->index(';') + 1); mAppearances.insertKeyAndValue(new UtlString(*callid_contact), new Appearance(getAppearanceAgent(), this, uri) ); } else { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::updateSubscriptions using existing subscription for '%s' in mUri = '%s'", callid_contact->data(), mSharedUser.data()); } } } // Free callid_contacts. callid_contacts.destroyAll(); }
// Destructor AppearanceGroup::~AppearanceGroup() { Os::Logger::instance().log(FAC_SAA, PRI_DEBUG, "AppearanceGroup::~ this = %p, mSharedUser = '******'", this, mSharedUser.data()); // Delete this AppearanceGroup from mSubscribeMap (for the "reg" subscription). getAppearanceAgent()->getAppearanceGroupSet().deleteSubscribeMapping(&mSubscriptionEarlyDialogHandle); // End the "reg" subscription. UtlBoolean ret; ret = getAppearanceAgent()->getSubscribeClient(). endSubscriptionGroup(mSubscriptionEarlyDialogHandle); Os::Logger::instance().log(FAC_SAA, ret ? PRI_INFO : PRI_WARNING, "AppearanceGroup::~ endSubscriptionGroup %s mSharedUser = '******', mSubscriptionEarlyDialogHandle = '%s'", ret ? "succeeded" : "failed", mSharedUser.data(), mSubscriptionEarlyDialogHandle.data()); // Remove this AppearanceGroup from mNotifyMap for all subscriptions. { UtlHashMapIterator itor(mSubscriptions); UtlString* handle; while ((handle = dynamic_cast <UtlString*> (itor()))) { getAppearanceAgent()->getAppearanceGroupSet().deleteNotifyMapping(handle); } } // Delete the contents of mSubscriptions. mSubscriptions.destroyAll(); // Terminate all dialogs for this shared Appearance Group, then publish bool bContentChanged = false; SipDialogEvent* lPartialContent = new SipDialogEvent("partial", mSharedUser.data()); { UtlHashMapIterator itor(mAppearances); UtlString* handle; while ( (handle = dynamic_cast <UtlString*> (itor())) ) { Appearance* pApp = dynamic_cast <Appearance*> (itor.value()); bContentChanged |= pApp->terminateDialogs(true); // terminate all dialogs pApp->getDialogs(lPartialContent); } } if (bContentChanged) { lPartialContent->buildBody(); publish(true, true, lPartialContent); } delete lPartialContent; // Delete the subordinate Appearance's for the contacts. { // Have to use a loop to remove items individually, because // destroyAll() deadlocks with the access to mAppearances // during the the attempt to publish new status for the resource // as the Appearances are recursively deleted. int changeDelay = getAppearanceAgent()->getChangeDelay(); UtlHashMapIterator itor(mAppearances); UtlContainable* k; while ((k = itor())) { UtlContainable* v = itor.value(); mAppearances.removeReference(k); delete k; delete v; // Delay to allow the consequent processing to catch up. OsTask::delay(changeDelay); } } }