'Updates presence of users. Sets the appropriate timeouts. Pokes
the notifier and federation if and only if the changed presence state
should be sent to clients/servers.'

@defer.inlineCallbacks
def _update_states(self, new_states):

    now = self.clock.time_msec()
    with Measure(self.clock, 'presence_update_states'):
        to_notify = {}
        to_federation_ping = {}
        new_states_dict = {}
        for new_state in new_states:
            new_states_dict[new_state.user_id] = new_state
        new_state = new_states_dict.values()
        for new_state in new_states:
            user_id = new_state.user_id
            prev_state = self.user_to_current_state.get(user_id, UserPresenceState.default(user_id))
            (new_state, should_notify, should_ping) = handle_update(prev_state, new_state, is_mine=self.is_mine_id(user_id), wheel_timer=self.wheel_timer, now=now)
            self.user_to_current_state[user_id] = new_state
            if should_notify:
                to_notify[user_id] = new_state
            elif should_ping:
                to_federation_ping[user_id] = new_state
        presence_updates_counter.inc_by(len(new_states))
        if to_notify:
            notified_presence_counter.inc_by(len(to_notify))
            (yield self._persist_and_notify(to_notify.values()))
        self.unpersisted_users_changes |= set((s.user_id for s in new_states))
        self.unpersisted_users_changes -= set(to_notify.keys())
        to_federation_ping = {user_id: state for (user_id, state) in to_federation_ping.items() if (user_id not in to_notify)}
        if to_federation_ping:
            federation_presence_out_counter.inc_by(len(to_federation_ping))
            self._push_to_remotes(to_federation_ping.values())
