www.pudn.com > d4j.zip > GCChannel.java
// local.dialogic.GCChannel
// Implements Global Call functionality, uses DialogicGC.dll
// $Id: GCChannel.java,v 1.6 2003/11/13 11:44:16 cgm8 Exp $
/*
* Copyright (c) 1999 Carlos G Mendioroz.
*
* This file is part of D4J.
*
* D4J 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; either
* version 2 of the License, or (at your option) any later version.
*
* D4J 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.
*
* Report problems and direct all questions to:
*
* tron@acm.org
*/
package local.dialogic;
public class GCChannel extends Channel implements Runnable {
// Constantes
private static final int RESET = 0;
private static final int IDLE = 1;
private static final int ICALL = 2;
private static final int RINGS = 3;
private static final int IN = 4;
private static final int OFFH = 5;
private static final int DIAL = 6;
private static final int OUT = 7;
//private static final int ANI = 8;
//private static final int SANI = 9;
private static final int ERROR = 10;
public static final int DT_GC = 0x800;
public static final int GCEV_TASKFAIL = (DT_GC | 0x01); /* Abnormal condition; state unchanged */
public static final int GCEV_ANSWERED = (DT_GC | 0x02); /* Call answered and connected */
public static final int GCEV_CALLPROGRESS= (DT_GC | 0x03);
public static final int GCEV_ACCEPT = (DT_GC | 0x04); /* Call is accepted */
public static final int GCEV_DROPCALL = (DT_GC | 0x05); /* gc_DropCall is completed */
public static final int GCEV_RESETLINEDEV= (DT_GC | 0x06); /* Restart event */
public static final int GCEV_CALLINFO = (DT_GC | 0x07); /* Info message received */
public static final int GCEV_REQANI = (DT_GC | 0x08); /* gc_ReqANI() is completed */
public static final int GCEV_SETCHANSTATE= (DT_GC | 0x09); /* gc_SetChanState() is completed */
public static final int GCEV_FACILITY_ACK= (DT_GC | 0x0A);
public static final int GCEV_FACILITY_REJ= (DT_GC | 0x0B);
public static final int GCEV_MOREDIGITS = (DT_GC | 0x0C); /* cc_moredigits() is completed*/
public static final int GCEV_SETBILLING = (DT_GC | 0x0E); /* gc_SetBilling() is completed */
public static final int GCEV_ALERTING = (DT_GC | 0x21); /* The destination telephone terminal
* equipment has received connection
* request (in ISDN accepted the
* connection request. This event is
* an unsolicited event
*/
public static final int GCEV_CONNECTED = (DT_GC | 0x22); /* Destination answered the request */
public static final int GCEV_ERROR = (DT_GC | 0x23); /* unexpected error event */
public static final int GCEV_OFFERED = (DT_GC | 0x24); /* A connection request has been made */
public static final int GCEV_DISCONNECTED= (DT_GC | 0x26); /* Remote end disconnected */
public static final int GCEV_PROCEEDING = (DT_GC | 0x27); /* The call state has been changed to
* the proceeding state */
public static final int GCEV_PROGRESSING = (DT_GC | 0x28); /* A call progress message has been
* received */
public static final int GCEV_USRINFO = (DT_GC | 0x29); /* A user to user information event is
* coming */
public static final int GCEV_FACILITYREQ = (DT_GC | 0x2A); /* A facility request is made by CO */
/* NB: ISDN equivalent value is */
/* CCEV_FACILITY */
public static final int GCEV_CONGESTION = (DT_GC | 0x2B); /* Remote end is not ready to accept
* incoming user information */
public static final int GCEV_FACILITY = (DT_GC | 0x2C); /* Facility info. available */
public static final int GCEV_D_CHAN_STATUS=(DT_GC | 0x2E); /* Report D-channel status to the user */
public static final int GCEV_NOUSRINFOBUF= (DT_GC | 0x30); /* User information element buffer is
* not ready */
public static final int GCEV_NOFACILITYBUF=(DT_GC | 0x31); /* Facility buffer is not ready */
public static final int GCEV_BLOCKED = (DT_GC | 0x32); /* Line device is blocked */
public static final int GCEV_UNBLOCKED = (DT_GC | 0x33); /* Line device is no longer blocked */
public static final int GCEV_ISDNMSG = (DT_GC | 0x34);
public static final int GCEV_NOTIFY = (DT_GC | 0x35); /* Notify message received */
public static final int GCEV_L2FRAME = (DT_GC | 0x36);
public static final int GCEV_L2BFFRFULL = (DT_GC | 0x37);
public static final int GCEV_L2NOBFFR = (DT_GC | 0x38);
public static final int GCEV_SETUP_ACK = (DT_GC | 0x39);
public static final int GCEV_CALLSTATUS = (DT_GC | 0x3A); /* call status, e.g. busy */
/*gc5*/
/* these events only apply to those sites using ISDN DPNSS */
public static final int GCEV_DIVERTED = (DT_GC | 0x40);
public static final int GCEV_HOLDACK = (DT_GC | 0x41);
public static final int GCEV_HOLDCALL = (DT_GC | 0x42);
public static final int GCEV_HOLDREJ = (DT_GC | 0x43);
public static final int GCEV_RETRIEVEACK = (DT_GC | 0x44);
public static final int GCEV_RETRIEVECALL= (DT_GC | 0x45);
public static final int GCEV_RETRIEVEREJ = (DT_GC | 0x46);
public static final int GCEV_NSI = (DT_GC | 0x47);
public static final int GCEV_TRANSFERACK = (DT_GC | 0x48);
public static final int GCEV_TRANSFERREJ = (DT_GC | 0x49);
public static final int GCEV_TRANSIT = (DT_GC | 0x4A);
/* end of ISDN DPNSS specific */
public static final int GCEV_ACKCALL = (DT_GC | 0x50); /* Termination event for gc_CallACK() */
/*
* Cause definitions for dropping a call
*/
public static final int GC_UNASSIGNED_NUMBER = 0x01; /* Number unassigned / unallocated */
public static final int GC_NORMAL_CLEARING = 0x10; /* Call dropped under normal conditions*/
public static final int GC_CHANNEL_UNACCEPTABLE= 0x06;
public static final int GC_USER_BUSY = 0x11; /* End user is busy */
public static final int GC_CALL_REJECTED = 0x15; /* Call was rejected */
public static final int GC_DEST_OUT_OF_ORDER = 0x19; /* Destination is out of order */
public static final int GC_NETWORK_CONGESTION = 0x2a;
public static final int GC_REQ_CHANNEL_NOT_AVAIL= 0x2c; /* Requested channel is not available */
public static final int GC_SEND_SIT = 0x300; /* send Special Info. Tone (SIT) */
/*
* RATE types for gc_SetBilling()
*/
public static final int GCR_CHARGE = 0x0000; /* Charge call (default) */
public static final int GCR_NOCHARGE = 0x0100; /* Do not charge call */
// Variables
private static Unloader loaded = null;
Voice voiceDev = null;
private GCLine lineDev = null;
private String name;
private int linestate = RESET;
private boolean cas = false;
private boolean blocked = true;
private boolean waiting = false;
private int crn = 0;
private String dnisPref = "";
// Native
// called via Dialogic loop for linux thread issue.
protected static native void gc_Start();
protected static native void gc_Stop();
protected static native int gc_Open(String name);
protected static native void gc_Close(int line);
protected static native int gc_GetVoiceH(int line);
protected static native int gc_GetNetworkH(int line);
protected static native void gc_Attach(int line, int voiceH, int mode);
protected static native void gc_Detach(int line, int voiceH, int mode);
protected static native int gc_WaitCall(int line, int timeout, int mode);
protected static native void gc_AcceptCall(int crn, int rings, int mode);
protected static native void gc_AnswerCall(int crn, int rings, int mode);
protected static native void gc_CallAck(int crn, int dnis, int mode);
protected static native void gc_DropCall(int crn, int cause, int mode);
protected static native int gc_GetDNIS(int crn, byte buf[]);
protected static native int gc_GetANI(int crn, byte buf[]);
protected static native void gc_GetCallInfo(int crn, int id, byte buf[]);
protected static native int gc_GetParm(int line, int id);
protected static native void gc_SetParm(int line, int id, int value);
protected static native int gc_MakeCall(int line, String number, int to, int mode);
protected static native void gc_ReleaseCall(int crn);
protected static native void gc_ResetLineDev(int line, int mode);
protected static native void gc_SetBilling(int crn, int rate, int mode);
protected static native void gc_SetChanState(int line, int state, int mode);
protected static native void gc_getMeta(EVT evt);
static {
try {
Class.forName("local.dialogic.Dialogic");
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Loading Dialogic class");
}
System.loadLibrary("DialogicGC");
loaded = new GCChannel().new Unloader();
}
// This class is a trick to get a class finalizer...
class Unloader {
protected void finalize() throws Throwable {
Dialogic.gc_Stop();
}
}
private GCChannel() {
name = "Unloader stub";
}
public GCChannel(String GCName) {
this(GCName, "");
}
public GCChannel(String GCName, String dnisPreffix) {
super();
if (!GC) {
Dialogic.gc_Start();
GC = true;
}
name = GCName;
dnisPref = dnisPreffix;
if (GCName.indexOf("P_isdn") >= 0) {
// independent voice implementations like ISDN
int vstart = GCName.indexOf(":V_");
if (vstart < 0)
throw new RuntimeException("No voice resource for ISDN");
int vend = GCName.indexOf(":", vstart + 2);
if (vend < 0)
vend = GCName.length();
String DxName = GCName.substring(vstart+3, vend);
GCName = name.substring(0, vstart) + name.substring(vend);
lineDev = new GCLine(this, GCName);
voiceDev = new Voice(this, DxName);
// link
lineDev.listen(voiceDev);
voiceDev.listen(lineDev);
} else {
// dependent voice implementations like R2CAS
lineDev = new GCLine(this, GCName);
voiceDev = lineDev.getVoice(this);
cas = true;
}
linestate = RESET;
group = new ThreadGroup(name + " group");
serviceThread = new Thread(group, this, name + " service");
serviceThread.start();
}
public void close()
{
super.clear();
if (serviceThread != null) {
Channel.stopGroup(this);
serviceThread.interrupt();
try {
serviceThread.join(500);
}
catch (InterruptedException ie) {};
serviceThread = null;
}
if (lineDev != null) {
lineDev.close();
lineDev = null;
}
if (voiceDev != null) {
voiceDev.close();
voiceDev = null;
}
}
public Voice getVoice() {
return voiceDev;
}
public Device getNetwork() {
return lineDev;
}
public void run() {
EVT evt;
while(true) try {
clear();
while (blocked)
evt = serviceWaitEvent();
if (!waiting) {
lineDev.waitCall();
waiting = true;
}
beginService();
while(true) {
evt = serviceWaitEvent();
if (evt.type == GCEV_OFFERED) {
linestate = ICALL;
setState(INCOMING);
crn = (int)evt.crn;
call = new Call(GCChannel.this);
byte dnis[] = new byte[10];
try {
int dnisl = Dialogic.gc_GetDNIS(crn, dnis);
call.dnis = dnisPref + new String(dnis, 0, dnisl);
} catch (RuntimeException rte) {
call.dnis = dnisPref;
}
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": Call to " + call.dnis + "(" + crn + ")");
if (handler != null) {
endService();
handler.handleCall(call);
}
} else {
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": Event " + evt);
}
}
}
catch (Exception e) {
if (linestate == DIAL)
// Someone asking me to leave...
return;
System.err.println(this.toString() + " service loop: " + e);
}
finally {
endService();
}
}
public String toString() {
return "GCChannel on " + name;
}
void accept() {
if (linestate != ICALL)
throw new ChannelException("accept(): wrong state");
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": accepting " + crn);
try {
beginService();
Dialogic.gc_AcceptCall(crn, 0, Dialogic.EV_ASYNC);
EVT evt = serviceWaitEvent();
if (evt.type != GCEV_ACCEPT)
throw new ChannelException("Accept error:" + evt);
linestate = RINGS;
} finally {
endService();
}
}
void answer(int rings) {
// It is ok to answer w/o accept...
if (!(linestate == RINGS || linestate == ICALL))
throw new ChannelException("answer(): wrong state");
if (linestate == ICALL)
accept();
// Ring cycle
EVT evt = null;
long now = System.currentTimeMillis();
long conn = call.startTime() + rings * 3000; // 3 secs each
try {
beginService();
if (conn > now)
evt = serviceWaitEvent(conn - now);
if (evt == null) {
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": answering " + crn);
Dialogic.gc_AnswerCall(crn, 0, Dialogic.EV_ASYNC);
evt = serviceWaitEvent();
if (evt.type != GCEV_ANSWERED)
throw new ChannelException("Answer error:" + evt);
linestate = IN;
} else {
linestate = ERROR;
setState(OOS);
throw new ChannelException("answer():" + evt);
}
} finally {
endService();
}
}
void reject(int reason) {
if (linestate != ICALL)
throw new ChannelException("reject(): wrong state");
throw new ChannelException("reject: not implemented!");
}
void dial(Call call, String number) {
if (linestate != IDLE)
throw new ChannelException("dial(): wrong state");
if (blocked)
throw new ChannelException("dial(): blocked");
linestate = DIAL;
if (serviceThread != null) {
Channel.stopGroup(this);
serviceThread.interrupt();
/* Service thread will normally die,
* but simultaneous seizure may prevent that from happening... */
try {
serviceThread.join(500);
}
catch(InterruptedException ie) {
Thread.currentThread().interrupt();
}
// Give up if it did not quit...
if (serviceThread.isAlive()) {
System.err.println("Seizure failure: service not quitting");
throw new ChannelException("dial(): seizure failure");
}
serviceThread = null;
}
this.call = call;
setState(OUTGOING);
// Wait for call completion...
try {
beginService();
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": dialing " + number);
crn = lineDev.dial(number, 30); // Note the 30 seconds timeout
EVT evt = serviceWaitEvent();
if (evt.type != GCEV_ALERTING)
throw new ChannelException("Dial error:" + evt);
} finally {
endService();
}
return;
}
String ani() {
String ani = "";
if (crn == 0)
return "";
byte anis[] = new byte[40];
try {
int anisl = Dialogic.gc_GetANI(crn,anis);
ani = new String(anis, 0, anisl);
} catch (Exception e) {
// Runtime exception thrown by GC layer if ani not available
ani = "";
}
return ani;
}
int pulses() {
throw new ChannelException("pulses: not implemented!");
}
synchronized void clear() {
if (linestate == RESET)
return;
linestate = RESET;
super.clear();
if (voiceDev != null) {
voiceDev.stop();
voiceDev.clear();
if (cas) {
lineDev.listen(voiceDev);
voiceDev.listen(lineDev);
}
}
if (crn != 0) {
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": dropping " + crn);
Dialogic.gc_DropCall(crn, GC_NORMAL_CLEARING, Dialogic.EV_ASYNC);
}
flush();
if (serviceThread == null) {
serviceThread = new Thread(group, this, name + " service");
serviceThread.start();
}
}
// Our line state service fn
protected EVT service(EVT evt) {
switch (evt.type) {
case GCEV_BLOCKED:
blocked = true;
waiting = false;
evt = null;
linestate = ERROR;
setState(OOS);
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": resetting");
lineDev.reset();
break;
case GCEV_UNBLOCKED:
blocked = false;
if (linestate == RESET) {
linestate = IDLE;
setState(FREE);
}
break;
case GCEV_DISCONNECTED:
voiceDev.stop();
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": dropping " + crn);
Dialogic.gc_DropCall(crn, GC_NORMAL_CLEARING, Dialogic.EV_ASYNC);
if (call != null) call.drop();
clear();
throw new HangUpException();
case GCEV_DROPCALL:
voiceDev.stop();
if ((Dialogic.debug & Dialogic.DEBUG_GC) != 0)
System.out.println(new java.util.Date().toString().substring(11,19) + this + ": releasing " + crn);
Dialogic.gc_ReleaseCall(crn);
if (call != null) call.drop();
crn = 0;
evt = null;
linestate = IDLE;
setState(FREE);
break;
case GCEV_CONNECTED:
// Remote answer
if (call != null) call.connect();
break; // Let the event go up
}
return evt;
}
}