/*
Copyright (C) 2004-2006 Nokia Corporation
Copyright (C) 2008-2011, Dirk Trossen, airs@dirk-trossen.de
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation as version 2.1 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.airs.platform;
import android.os.Bundle;
import android.os.Message;
import com.airs.AIRS_remote;
import com.airs.helper.SerialPortLogger;
/**
* Class that handles all incoming {@link Method} types
*/
public class EventComponent implements Runnable
{
private DIALOG_INFO incoming=null, outgoing=null;
private EVENT_UA event_ua=null;
private TCPClient current_TCPClient=null;
private short dialog_id= 0;
private Thread thread = null;
/**
* Reference to {@link AIRS_remote} service
*/
public AIRS_remote airs;
/**
* Flag if connected (true) or not (false)
*/
public boolean connected = false;
private void debug(String msg)
{
SerialPortLogger.debug(msg);
}
/**
* Sleep function
* @param millis
*/
private void sleep(long millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException ignore)
{
}
}
/**
* Start the EventComponent after being instantiated
* @param airs Reference to the {@link AIRS_remote} service which started this component
* @param IPAddress String with the IP address to connect to
* @param IPPort Port to connect to at the given IP address
*/
public boolean startEC(AIRS_remote airs, String IPAddress, String IPPort)
{
current_TCPClient = new TCPClient();
// was connection successful?
if (current_TCPClient.startTCP(airs, IPAddress, IPPort) == false)
return false;
connected = true;
this.airs = airs;
// start receiving thread in EventComponent
thread = new Thread(this);
thread.start();
return true;
}
/**
* Runnable thread of this class for receiving methods from remote application server
*/
public void run()
{
Method current_method;
// keep on running
while (connected == true)
{
while(current_TCPClient.connected == false)
sleep(250);
try
{
// signal connection to Acquisition and Discovery
connected = true;
// read method from TCP connection
current_method = current_TCPClient.read();
// if one came in -> dispatch
if (Dispatch(current_method)==false)
debug("Error in dispatching incoming method");
}
catch (Exception e)
{
current_TCPClient.disconnect();
if (connected == true)
{
Message msg = airs.mHandler.obtainMessage(AIRS_remote.SHOW_NOTIFICATION);
Bundle bundle = new Bundle();
bundle.putString(AIRS_remote.TEXT, "It seems that the TCP connection broke. It is recommended to restart the gateway since the server might be temporarily down!");
msg.setData(bundle);
airs.mHandler.sendMessage(msg);
}
SerialPortLogger.debugForced("CONNECTION FAILURE: Restart gateway!");
return;
}
}
}
/**
* Stop the EventComponent
*/
public void stop()
{
// signal thread that being disconnected -> will disconnect TCP Client and show alerter
if (thread!=null)
thread.interrupt();
current_TCPClient.disconnect();
}
/**
* Register an event server with a given Callback for a given event name
* @param callback Reference to the {@link Callback} for this event
* @param event_name_string String for the event name being registered
* @return true, if successful
*/
synchronized public boolean registerEventServer(Callback callback, String event_name_string)
{
EVENT_UA new_UA = null;
byte event_name[];
event_name = event_name_string.getBytes();
if (find_EventUA(event_name)!=null)
{
debug("EventComponent::registerEventServer(): event name already registered!");
return false;
}
// create new event UA structure
new_UA = new EVENT_UA();
// fill in the fields
new_UA.event_name = event_name;
new_UA.callback = callback;
new_UA.next = null;
// now insert in list
// first one?
if (event_ua == null)
event_ua = new_UA;
else
{
new_UA.next = event_ua;
event_ua = new_UA;
}
return true;
}
/**
* Establishes a SUBSCRIBE dialog
* @param TO byte array with the name to send this to (usually 'application server')
* @param event_name byte array with the event name being subscribed to
* @param event_body byte array with the event body
* @param body_length length of the event body array
* @param Expires time to expire in milliseconds
* @param callback Reference to the {@link Callback} for this dialog
* @return DIALOG_INFO of the established dialog
*/
synchronized public DIALOG_INFO Subscribe(byte TO[], byte event_name[], byte event_body[], int body_length, int Expires, Callback callback)
{
Method method = new Method();
DIALOG_INFO new_dialog=null;
// not connected?
if (current_TCPClient.connected == false)
return null;
// fill fields of methods
method.method_type = method_type.method_SUBSCRIBE;
method.FROM.string = current_TCPClient.IMEI.getBytes();
method.FROM.length = (short)current_TCPClient.IMEI.length();
method.TO.string = TO;
method.TO.length = (short)TO.length;
method.event_name.string = event_name;
method.event_name.length = (short)event_name.length;
method.event_body.string = event_body;
method.event_body.length = event_body.length;
method.sub.Expires = Expires;
// create new dialog for this subscription
if ((new_dialog = create_Dialog(method, callback, dialog_state.SUBSCRIPTION_PENDING, false)) != null)
{
// try to send the method
if (current_TCPClient.write(method) == true)
return new_dialog;
else
delete_Dialog(new_dialog, false);
}
else
debug("EventComponent::Subscribe(): cannot allocate memory for Method");
return null;
}
/**
* Sends notification in subscription dialog
* @param dialog_id identifier for the dialog being used
* @param event_body byte array with the event body being sent
* @param callback Reference to the {@link Callback} being notified upon completion
* @return true, if successful
*/
synchronized public boolean Notify(short dialog_id, byte event_body[], Callback callback)
{
DIALOG_INFO current_dialog;
// not connected?
if (current_TCPClient.connected == false)
return false;
debug("EventComponent::Notify:invoked NOTIFY");
if ((current_dialog = find_Dialog(dialog_id, true))!=null)
{
// only allow to send if subscription is valid or another notification has been sent (which means that subscription is valid)
if ((current_dialog.dialog_state == dialog_state.SUBSCRIPTION_VALID)
||(current_dialog.dialog_state == dialog_state.NOTIFICATION_SENT))
{
// wait until the dialog is unlocked!!
while (current_dialog.locked == true)
sleep(100);
// if dialog is terminated now, return (might be incoming BYE!)
if (current_dialog.dialog_state == dialog_state.SUBSCRIPTION_TERMINATED_CLIENT)
return false;
// lock dialog!
current_dialog.locked = true;
// set method type to NOTIFY
debug("...set method to NOTIFY");
current_dialog.current_method.method_type = method_type.method_NOTIFY;
// fill in FROM and TO
debug("...fill in FROM and TO");
current_dialog.current_method.FROM.string = current_TCPClient.IMEI.getBytes();
current_dialog.current_method.FROM.length = (short)current_TCPClient.IMEI.length();
current_dialog.current_method.TO.string = current_dialog.peer.string;
current_dialog.current_method.TO.length = current_dialog.peer.length;
// fill in event body
debug("...fill in event body");
current_dialog.current_method.event_body.string = event_body;
current_dialog.current_method.event_body.length = event_body.length;
// copy dialog_id and increase CSeq
current_dialog.current_method.not.dialog_id = dialog_id;
// always increase CSeq in subcription part of the method structure!
current_dialog.CSeq++;
// and now copy over to notification part of the method structure!
current_dialog.current_method.not.CSeq = current_dialog.CSeq;
debug("...sending NOTIFY");
// track values being sent
airs.values_sent++;
// try to send the method
if (current_TCPClient.write(current_dialog.current_method) == true)
{
// now set the dialog state correctly
current_dialog.dialog_state = dialog_state.NOTIFICATION_SENT;
// free dialog
current_dialog.locked = false;
debug("...done");
return true;
}
else
{
// free dialog
current_dialog.locked = false;
debug("EventComponent::Notify(): Error in sending method");
}
}
else
debug("EventComponent::Notify(): wrong dialog state");
}
else
debug("EventComponent::Notify(): cannot find dialog for this dialog_id");
return false;
}
/**
* Establishes a new PUBLISH dialog
* @param TO byte array with the name to send this to (usually 'application server')
* @param event_name byte array with the event name being subscribed to
* @param event_body byte array with the event body
* @param Expires time to expire in milliseconds
* @param callback Reference to the {@link Callback} for this dialog
* @return DIALOG_INFO of the established dialog
*/
synchronized public DIALOG_INFO Publish(byte TO[], byte event_name[], byte event_body[], int Expires, Callback callback)
{
Method method = new Method();
DIALOG_INFO new_dialog = null;
// not connected?
if (current_TCPClient.connected == false)
return null;
// fill fields of methods
method.method_type = method_type.method_PUBLISH;
method.FROM.string = current_TCPClient.IMEI.getBytes();
method.FROM.length = (short)current_TCPClient.IMEI.length();
method.TO.string = TO;
method.TO.length = (short)TO.length;
method.event_name.string = event_name;
method.event_name.length = (short)event_name.length;
method.event_body.string = event_body;
method.event_body.length = event_body.length;
method.pub.Expires = Expires;
// create new dialog for this subscription
if ((new_dialog = create_Dialog(method, callback, dialog_state.PUBLICATION_PENDING, false)) != null)
{
// try to send the method
if (current_TCPClient.write(method) == true)
return new_dialog;
else
delete_Dialog(new_dialog, false);
}
else
debug("EventComponent::Publish(): cannot allocate memory for Method");
return null;
}
/**
* Sends a PUBLISH method in an existing dialog
* @param dialog Reference to the {@link DIALOG_INFO} that holds the dialog information to be used
* @param TO byte array with the name to send this to (usually 'application server')
* @param event_name byte array with the event name being subscribed to
* @param event_body byte array with the event body
* @param Expires time to expire in milliseconds
* @return true, if successful
*/
synchronized public boolean Publish(DIALOG_INFO dialog, byte TO[], byte event_name[], byte event_body[], int Expires)
{
// not connected?
if (current_TCPClient.connected == false)
return false;
// fill fields of methods
dialog.current_method.method_type = method_type.method_PUBLISH;
dialog.current_method.FROM.string = current_TCPClient.IMEI.getBytes();
dialog.current_method.FROM.length = (short)current_TCPClient.IMEI.length();
dialog.current_method.TO.string = TO;
dialog.current_method.TO.length = (short)TO.length;
dialog.current_method.event_name.string = event_name;
dialog.current_method.event_name.length = (short)event_name.length;
dialog.current_method.event_body.string = event_body;
dialog.current_method.event_body.length = event_body.length;
dialog.current_method.pub.Expires = Expires;
dialog.current_method.pub.e_tag = (int)dialog.dialog_id;
dialog.dialog_state = dialog_state.PUBLICATION_PENDING;
// try to send the method
return current_TCPClient.write(dialog.current_method);
}
/**
* sends confirmation outside of any existing dialog, using the information from the old method
* @param method Reference to the {@link Method} being re-used
* @param ret_code Return code being inserted into the CONFIRM method
* @return true, if successful
*/
synchronized public boolean Confirm(Method method, String ret_code)
{
byte swap[];
short swap_length;
boolean return_code;
// swap FROM and TO
swap = method.FROM.string;
method.FROM.string = method.TO.string;
method.TO.string = swap;
swap_length = method.FROM.length;
method.FROM.length = method.TO.length;
method.TO.length = swap_length;
switch(method.method_type)
{
case method_type.method_SUBSCRIBE:
// copy dialog identifier from original request and use sequence number plus one
method.conf.dialog.dialog_id = method.sub.dialog_id;
method.conf.dialog.CSeq = (short)(method.sub.CSeq + 1);
break;
case method_type.method_NOTIFY:
// copy dialog identifier from original request and use sequence number plus one
method.conf.dialog.dialog_id = method.not.dialog_id;
method.conf.dialog.CSeq = (short)(method.not.CSeq + 1);
break;
case method_type.method_BYE:
// copy dialog identifier from original request and use sequence number plus one
method.conf.dialog.dialog_id = method.BYE.dialog_id;
method.conf.dialog.CSeq = (short)(method.BYE.CSeq + 1);
break;
case method_type.method_PUBLISH:
// copy dialog identifier from original request and use sequence number plus one
method.conf.e_tag = method.pub.e_tag;
break;
}
if (method.method_type == method_type.method_PUBLISH)
method.conf.confirm_type = confirm_type.confirm_PUBLISH;
else
method.conf.confirm_type = confirm_type.confirm_OTHERS;
method.conf.Expires = 0;
method.conf.ret_code.string = ret_code.getBytes();
method.conf.ret_code.length = (short)ret_code.length();
// there is an error -> form method for negative confirmation
method.method_type = method_type.method_CONFIRM;
return_code = current_TCPClient.write(method);
return return_code;
}
/**
* sends confirmation within existing dialog
* @param dialog_info Reference to the {@link DIALOG_INFO} of the dialog being used
* @param ret_code Return code being inserted into the CONFIRM method
* @return true, if successful
*/
synchronized public boolean Confirm(DIALOG_INFO dialog_info, String ret_code)
{
boolean return_code;
// wait until the dialog is unlocked!!
while (dialog_info.locked == true)
sleep(100);
debug("EventComponent::Confirm:send out confirmation");
// fill in FROM and TO
dialog_info.current_method.FROM.string = current_TCPClient.IMEI.getBytes();
dialog_info.current_method.FROM.length = (short)current_TCPClient.IMEI.length();
dialog_info.current_method.TO.string = dialog_info.peer.string;
dialog_info.current_method.TO.length = dialog_info.peer.length;
dialog_info.CSeq++;
switch(dialog_info.current_method.method_type)
{
case method_type.method_SUBSCRIBE:
debug("EventComponent::Confirm:method is SUBSRIBE");
// copy dialog identifier from original request and use sequence number plus one
dialog_info.current_method.conf.dialog.dialog_id = dialog_info.current_method.sub.dialog_id;
dialog_info.current_method.conf.dialog.CSeq = dialog_info.CSeq;
break;
case method_type.method_NOTIFY:
debug("EventComponent::Confirm:method is NOTIFY");
// copy dialog identifier from original request and use sequence number plus one
dialog_info.current_method.conf.dialog.dialog_id = dialog_info.current_method.not.dialog_id;
dialog_info.current_method.conf.dialog.CSeq = dialog_info.CSeq;
break;
case method_type.method_BYE:
// copy dialog identifier from original request and use sequence number plus one
dialog_info.current_method.conf.dialog.dialog_id = dialog_info.current_method.BYE.dialog_id;
dialog_info.current_method.conf.dialog.CSeq = dialog_info.CSeq;
break;
case method_type.method_PUBLISH:
// copy dialog identifier from original request and use sequence number plus one
dialog_info.current_method.conf.e_tag = dialog_info.current_method.pub.e_tag;
break;
}
if (dialog_info.current_method.method_type == method_type.method_PUBLISH)
dialog_info.current_method.conf.confirm_type = confirm_type.confirm_PUBLISH;
else
dialog_info.current_method.conf.confirm_type = confirm_type.confirm_OTHERS;
dialog_info.current_method.conf.Expires = (int)0;
dialog_info.current_method.conf.ret_code.string = ret_code.getBytes();
dialog_info.current_method.conf.ret_code.length = (short)ret_code.length();
dialog_info.current_method.method_type = method_type.method_CONFIRM;
dialog_info.current_method.event_body.string = null;
dialog_info.current_method.event_body.length = 0;
return_code = current_TCPClient.write(dialog_info.current_method);
return return_code;
}
/**
* Terminates existing dialog
* @param dialog Reference to the {@link DIALOG_INFO} of the dialog to be terminated
* @param callback Reference to the {@link Callback} of this dialog
* @return true, if successful
*/
synchronized public boolean Bye(DIALOG_INFO dialog, Callback callback)
{
return true;
}
/**
* Dispatches incoming method
* @param Reference to the {@link Method} being dispatched
* @return true, if successful
*/
boolean Dispatch(Method current_method)
{
short dialog_id;
boolean ret_value = true, direction;
DIALOG_INFO current_dialog;
Callback current_callback;
debug("EventComponent::Dispatch:dispatch newly arrived method");
// handle different methods
switch(current_method.method_type)
{
case method_type.method_SUBSCRIBE:
debug("...it's a SUBSCRIBE");
// is there an existing dialog?
if ((current_dialog = find_Dialog(current_method.sub.dialog_id, true)) != null)
{
// is the existing dialog a subscription that is valid -> re-subscription otherwise it is an error!!!
if ((current_dialog.dialog_state == dialog_state.SUBSCRIPTION_VALID) || (current_dialog.dialog_state == dialog_state.NOTIFICATION_SENT))
{
current_dialog.current_method = current_method; // store last received method
current_dialog.locked = true; // lock dialog
current_dialog.callback.callback(current_dialog); // and call dialog callback
}
else
Confirm(current_method, Ret_Codes.RC_400_BAD_REQUEST);
}
else
{
debug("...it's outside an existing dialog");
// try to find the event UA for the incoming method
if ((current_callback = find_EventUA(current_method.event_name.string)) != null)
{
// create a new dialog for this incoming one
if ((current_dialog= create_Dialog(current_method, current_callback, dialog_state.SUBSCRIPTION_PENDING, true))!= null)
{
current_dialog.locked = true; // lock dialog
current_callback.callback(current_dialog);
}
}
else
{
Confirm(current_method, Ret_Codes.RC_489_BAD_EVENT);
}
}
break;
case method_type.method_NOTIFY:
debug("...it's a NOTIFY");
// is there an existing incoming dialog?
if ((current_dialog = find_Dialog(current_method.not.dialog_id, false)) != null)
{
// has anything been sent before (SUBSCRIBE, NOTIFY, BYE, or PUBLISH)?
if ((current_dialog.dialog_state == dialog_state.SUBSCRIPTION_VALID))
{
current_dialog.current_method = current_method; // store last received method
current_dialog.locked = true; // lock dialog
current_dialog.callback.callback(current_dialog); // and call dialog callback
}
else
{
Confirm(current_method, Ret_Codes.RC_4xx_BAD_STATE);
debug("EventComponent::Dispatch(): received NOTIFY in wrong state -> discard");
}
}
else
{
Confirm(current_method, Ret_Codes.RC_404_NOT_FOUND);
debug("EventComponent::Dispatch(): received NOTIFY without dislog -> discard");
}
break;
case method_type.method_PUBLISH:
debug("...it's a PUBLISH");
break;
case method_type.method_CONFIRM:
debug("...it's a CONFIRM");
if (current_method.conf.confirm_type == confirm_type.confirm_OTHERS)
{
dialog_id = current_method.conf.dialog.dialog_id;
direction = true;
}
else
{
dialog_id = (short)current_method.conf.e_tag;
direction = false; // publication is only outgoing dialog!!
}
// is CONFIRM for outgoing dialog?
if (direction==false)
{
// is there an existing outgoing dialog (PUBLISH)?
if ((current_dialog = find_Dialog(dialog_id, false)) != null)
{
// has anything been sent before (SUBSCRIBE, NOTIFY, BYE, or PUBLISH)?
if ((current_dialog.dialog_state == dialog_state.SUBSCRIPTION_PENDING)
||(current_dialog.dialog_state == dialog_state.PUBLICATION_PENDING)
||(current_dialog.dialog_state == dialog_state.SUBSCRIPTION_TERMINATED_CLIENT))
{
current_dialog.current_method = current_method; // store last received method
current_dialog.locked = true; // lock dialog
current_dialog.callback.callback(current_dialog); // and call dialog callback
}
else
debug("EventComponent::Dispatch(): received CONFIRM out-of-band -> discard");
}
}
else // or is it for incoming dialog
{
// is there an existing incoming dialog?
if ((current_dialog = find_Dialog(dialog_id, true)) != null)
{
// has anything been sent before (SUBSCRIBE, NOTIFY, BYE, or PUBLISH)?
if ((current_dialog.dialog_state == dialog_state.NOTIFICATION_SENT)
||(current_dialog.dialog_state == dialog_state.SUBSCRIPTION_TERMINATED_SERVER))
{
current_dialog.current_method = current_method; // store last received method
current_dialog.locked = true; // lock dialog
current_dialog.callback.callback(current_dialog); // and call dialog callback
}
else
debug("EventComponent::Dispatch(): received CONFIRM out-of-band -> discard");
}
else
debug("EventComponent::Dispatch(): received CONFIRM out-of-band -> discard");
}
break;
case method_type.method_BYE:
debug("...it's a BYE");
// is there an existing incoming dialog?
if ((current_dialog = find_Dialog(current_method.BYE.dialog_id, true)) != null)
{
debug("...found dialog for it");
while (current_dialog.locked == true)
sleep(100);
// lock dialog
current_dialog.locked = true;
// has anything been sent before (SUBSCRIBE, NOTIFY, BYE, or PUBLISH)?
if ((current_dialog.dialog_state == dialog_state.SUBSCRIPTION_VALID)
||(current_dialog.dialog_state == dialog_state.PUBLICATION_VALID)
||(current_dialog.dialog_state == dialog_state.NOTIFICATION_SENT))
{
debug("...call callback for BYE method");
current_dialog.current_method = current_method; // store last received method
current_dialog.callback.callback(current_dialog); // and call dialog callback
delete_Dialog(current_dialog, true); // delete dialog from list
}
else
debug("EventComponent::Dispatch(): received BYE out-of-band -> discard");
}
else
debug("EventComponent::Dispatch(): received BYE out-of-band -> discard");
break;
default: // shouldn't come though
ret_value = false;
break;
}
return ret_value;
}
/**
* creates new dialog:
* for incoming dialogs, we'll take the dialog id that came with the method
* for outgoing dialogs, we create a dialog id bound to the TCP client
* Hence, the dialog id is always set by the client (subscriber or publisher)!
* @param method Reference to the {@link Method} that creates the dialog
* @param callback Reference to the {@link Callback} for this dialog
* @param state initial state of the dialog
* @param direction true, if outgoing, false if incoming
* @return Reference to a {@link DIALOG_INFO} that has been created for this new dialog
*/
DIALOG_INFO create_Dialog(Method method, Callback callback, short state, boolean direction)
{
DIALOG_INFO new_insert = new DIALOG_INFO();
// copy fields
new_insert.current_method = method;
// if incoming dialog -> FROM is peer
if (direction==true)
new_insert.peer.string = method.FROM.string;
else
new_insert.peer.string = method.TO.string;
new_insert.peer.length = (short)new_insert.peer.string.length;
new_insert.callback = callback;
new_insert.next = null;
new_insert.dialog_state = state;
new_insert.CSeq = (short)0;
// insert in different queue, depending on direction
if (direction == true)
{
// copy dialog id
if (method.method_type == method_type.method_SUBSCRIBE)
new_insert.dialog_id = method.sub.dialog_id;
else
new_insert.dialog_id = (short)method.pub.e_tag;
if (incoming != null)
{
new_insert.next = incoming;
incoming = new_insert;
}
else
incoming = new_insert;
}
else
{
// create new dialog id for this client, note that only SUBSCRIBE and PUBLISH can create dialogs!!
dialog_id++;
new_insert.current_method.sub.dialog_id = dialog_id;
new_insert.current_method.sub.CSeq = 0;
new_insert.current_method.pub.e_tag = (int)dialog_id;
new_insert.dialog_id = dialog_id;
if (outgoing != null)
{
new_insert.next = outgoing;
outgoing = new_insert;
}
else
outgoing = new_insert;
}
return new_insert;
}
/**
* Deletes dialog information
* @param dialog Reference to the {@link DIALOG_INFO} being deleted
* @param direction Direction of the dialog with true for outgoing, false for incoming
*/
void delete_Dialog(DIALOG_INFO dialog, boolean direction)
{
DIALOG_INFO last, search;
// for incoming dialogs?
if (direction == true)
last=search = incoming;
else
last=search = outgoing;
while(search != null)
{
// found dialog?
if (search==dialog)
{
// is dialog the first one?
if (last == search)
{
if (direction == true)
incoming = incoming.next;
else
outgoing = outgoing.next;
}
else
last.next = search.next;
return;
}
else
{
last = search;
search = search.next;
}
}
}
/**
* Finds dialog information that matches dialog_id
* @param dialog_id identifier of the dialog to be searched for
* @param direction Direction of the dialog with true for outgoing, false for incoming
* @return Reference to the {@link DIALOG_INFO} being found
*/
DIALOG_INFO find_Dialog(short dialog_id, boolean direction)
{
DIALOG_INFO search;
// for incoming dialogs?
if (direction == true)
search = incoming;
else
search = outgoing;
while(search!=null)
{
// dialog identifier is stored in either subscription, notification or confirmation method
// but look for matching TCP clients since dialog_ids are only unique for particular TCP client
if (search.dialog_id == dialog_id)
return search;
search = search.next;
}
return null;
}
/**
* Finds callback for event name -> used for incoming subscription
* @param event_name byte array of the event name whose UA is to be found
* @return Reference to {@link Callback}
*/
Callback find_EventUA(byte event_name[])
{
EVENT_UA search = event_ua;
int i, length = event_name.length;
boolean found;
while(search != null)
{
// string comparison is done here!
// is length of names already the same??
if (length == search.event_name.length)
{
// mark as found
found = true;
for (i = 0; i<length ; i++)
if (event_name[i]!=search.event_name[i])
found=false; // did not match -> mark as not found
// if found -> return callback
if (found == true)
return search.callback;
}
search = search.next;
}
return null;
}
}