1
/* $Id: MyApp.java 4734 2014-02-05 09:32:57Z nanang $ */
3
* Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
package org.pjsip.pjsua2.app;
22
import java.util.ArrayList;
23
import org.pjsip.pjsua2.*;
26
/* Interface to separate UI & engine a bit better */
27
interface MyAppObserver {
28
abstract void notifyRegState(pjsip_status_code code, String reason, int expiration);
29
abstract void notifyIncomingCall(MyCall call);
30
abstract void notifyCallState(MyCall call);
31
abstract void notifyBuddyState(MyBuddy buddy);
35
class MyLogWriter extends LogWriter {
37
public void write(LogEntry entry) {
38
System.out.println(entry.getMsg());
43
class MyCall extends Call {
44
MyCall(MyAccount acc, int call_id) {
49
public void onCallState(OnCallStateParam prm) {
50
MyApp.observer.notifyCallState(this);
54
public void onCallMediaState(OnCallMediaStateParam prm) {
58
} catch (Exception e) {
62
CallMediaInfoVector cmiv = ci.getMedia();
64
for (int i = 0; i < cmiv.size(); i++) {
65
CallMediaInfo cmi = cmiv.get(i);
66
if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO &&
67
(cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE ||
68
cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD))
70
// unfortunately, on Java too, the returned Media cannot be downcasted to AudioMedia
71
Media m = getMedia(i);
72
AudioMedia am = AudioMedia.typecastFromMedia(m);
76
MyApp.ep.audDevManager().getCaptureDevMedia().startTransmit(am);
77
am.startTransmit(MyApp.ep.audDevManager().getPlaybackDevMedia());
78
} catch (Exception e) {
87
class MyAccount extends Account {
88
public ArrayList<MyBuddy> buddyList = new ArrayList<MyBuddy>();
89
public AccountConfig cfg;
91
MyAccount(AccountConfig config) {
96
public MyBuddy addBuddy(BuddyConfig bud_cfg)
99
MyBuddy bud = new MyBuddy(bud_cfg);
101
bud.create(this, bud_cfg);
102
} catch (Exception e) {
108
if (bud_cfg.getSubscribe())
110
bud.subscribePresence(true);
111
} catch (Exception e) {}
117
public void delBuddy(MyBuddy buddy) {
118
buddyList.remove(buddy);
121
public void delBuddy(int index) {
122
buddyList.remove(index);
126
public void onRegState(OnRegStateParam prm) {
127
MyApp.observer.notifyRegState(prm.getCode(), prm.getReason(), prm.getExpiration());
131
public void onIncomingCall(OnIncomingCallParam prm) {
132
System.out.println("======== Incoming call ======== ");
133
MyCall call = new MyCall(this, prm.getCallId());
134
MyApp.observer.notifyIncomingCall(call);
138
public void onInstantMessage(OnInstantMessageParam prm) {
139
System.out.println("======== Incoming pager ======== ");
140
System.out.println("From : " + prm.getFromUri());
141
System.out.println("To : " + prm.getToUri());
142
System.out.println("Contact : " + prm.getContactUri());
143
System.out.println("Mimetype : " + prm.getContentType());
144
System.out.println("Body : " + prm.getMsgBody());
149
class MyBuddy extends Buddy {
150
public BuddyConfig cfg;
152
MyBuddy(BuddyConfig config) {
157
String getStatusText() {
162
} catch (Exception e) {
167
if (bi.getSubState() == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) {
168
if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE) {
169
status = bi.getPresStatus().getStatusText();
170
if (status == null || status.isEmpty()) {
173
} else if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE) {
183
public void onBuddyState() {
184
MyApp.observer.notifyBuddyState(this);
190
class MyAccountConfig {
191
public AccountConfig accCfg = new AccountConfig();
192
public ArrayList<BuddyConfig> buddyCfgs = new ArrayList<BuddyConfig>();
194
public void readObject(ContainerNode node) {
196
ContainerNode acc_node = node.readContainer("Account");
197
accCfg.readObject(acc_node);
198
ContainerNode buddies_node = acc_node.readArray("buddies");
200
while (buddies_node.hasUnread()) {
201
BuddyConfig bud_cfg = new BuddyConfig();
202
bud_cfg.readObject(buddies_node);
203
buddyCfgs.add(bud_cfg);
205
} catch (Exception e) {}
208
public void writeObject(ContainerNode node) {
210
ContainerNode acc_node = node.writeNewContainer("Account");
211
accCfg.writeObject(acc_node);
212
ContainerNode buddies_node = acc_node.writeNewArray("buddies");
213
for (int j = 0; j < buddyCfgs.size(); j++) {
214
buddyCfgs.get(j).writeObject(buddies_node);
216
} catch (Exception e) {}
223
System.loadLibrary("pjsua2");
224
System.out.println("Library loaded");
227
public static Endpoint ep = new Endpoint();
228
public static MyAppObserver observer;
229
public ArrayList<MyAccount> accList = new ArrayList<MyAccount>();
231
private ArrayList<MyAccountConfig> accCfgs = new ArrayList<MyAccountConfig>();
232
private EpConfig epConfig = new EpConfig();
233
private TransportConfig sipTpConfig = new TransportConfig();
234
private String appDir;
236
/* Maintain reference to log writer to avoid premature cleanup by GC */
237
private MyLogWriter logWriter;
239
private final String configName = "pjsua2.json";
240
private final int SIP_PORT = 6000;
241
private final int LOG_LEVEL = 4;
243
public void init(MyAppObserver obs, String app_dir) {
244
init(obs, app_dir, false);
247
public void init(MyAppObserver obs, String app_dir, boolean own_worker_thread) {
251
/* Create endpoint */
254
} catch (Exception e) {
260
String configPath = appDir + "/" + configName;
261
File f = new File(configPath);
263
loadConfig(configPath);
265
/* Set 'default' values */
266
sipTpConfig.setPort(SIP_PORT);
269
/* Override log level setting */
270
epConfig.getLogConfig().setLevel(LOG_LEVEL);
271
epConfig.getLogConfig().setConsoleLevel(LOG_LEVEL);
273
/* Set log config. */
274
LogConfig log_cfg = epConfig.getLogConfig();
275
logWriter = new MyLogWriter();
276
log_cfg.setWriter(logWriter);
277
log_cfg.setDecor(log_cfg.getDecor() &
278
~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() |
279
pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue()));
282
UaConfig ua_cfg = epConfig.getUaConfig();
283
ua_cfg.setUserAgent("Pjsua2 Android " + ep.libVersion().getFull());
284
StringVector stun_servers = new StringVector();
285
stun_servers.add("stun.pjsip.org");
286
ua_cfg.setStunServer(stun_servers);
287
if (own_worker_thread) {
288
ua_cfg.setThreadCnt(0);
289
ua_cfg.setMainThreadOnly(true);
294
ep.libInit(epConfig);
295
} catch (Exception e) {
299
/* Create transports. */
301
ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig);
302
} catch (Exception e) {
303
System.out.println(e);
307
ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, sipTpConfig);
308
} catch (Exception e) {
309
System.out.println(e);
312
/* Create accounts. */
313
for (int i = 0; i < accCfgs.size(); i++) {
314
MyAccountConfig my_cfg = accCfgs.get(i);
315
MyAccount acc = addAcc(my_cfg.accCfg);
320
for (int j = 0; j < my_cfg.buddyCfgs.size(); j++) {
321
BuddyConfig bud_cfg = my_cfg.buddyCfgs.get(j);
322
acc.addBuddy(bud_cfg);
329
} catch (Exception e) {
334
public MyAccount addAcc(AccountConfig cfg) {
335
MyAccount acc = new MyAccount(cfg);
338
} catch (Exception e) {
347
public void delAcc(MyAccount acc) {
351
private void loadConfig(String filename) {
352
JsonDocument json = new JsonDocument();
356
json.loadFile(filename);
357
ContainerNode root = json.getRootContainer();
359
/* Read endpoint config */
360
epConfig.readObject(root);
362
/* Read transport config */
363
ContainerNode tp_node = root.readContainer("SipTransport");
364
sipTpConfig.readObject(tp_node);
366
/* Read account configs */
368
ContainerNode accs_node = root.readArray("accounts");
369
while (accs_node.hasUnread()) {
370
MyAccountConfig acc_cfg = new MyAccountConfig();
371
acc_cfg.readObject(accs_node);
372
accCfgs.add(acc_cfg);
374
} catch (Exception e) {
375
System.out.println(e);
378
/* Force delete json now, as I found that Java somehow destroys it
379
* after lib has been destroyed and from non-registered thread.
384
private void buildAccConfigs() {
385
/* Sync accCfgs from accList */
387
for (int i = 0; i < accList.size(); i++) {
388
MyAccount acc = accList.get(i);
389
MyAccountConfig my_acc_cfg = new MyAccountConfig();
390
my_acc_cfg.accCfg = acc.cfg;
392
my_acc_cfg.buddyCfgs.clear();
393
for (int j = 0; j < acc.buddyList.size(); j++) {
394
MyBuddy bud = acc.buddyList.get(j);
395
my_acc_cfg.buddyCfgs.add(bud.cfg);
398
accCfgs.add(my_acc_cfg);
402
private void saveConfig(String filename) {
403
JsonDocument json = new JsonDocument();
406
/* Write endpoint config */
407
json.writeObject(epConfig);
409
/* Write transport config */
410
ContainerNode tp_node = json.writeNewContainer("SipTransport");
411
sipTpConfig.writeObject(tp_node);
413
/* Write account configs */
415
ContainerNode accs_node = json.writeNewArray("accounts");
416
for (int i = 0; i < accCfgs.size(); i++) {
417
accCfgs.get(i).writeObject(accs_node);
421
json.saveFile(filename);
422
} catch (Exception e) {}
424
/* Force delete json now, as I found that Java somehow destroys it
425
* after lib has been destroyed and from non-registered thread.
430
public void deinit() {
431
String configPath = appDir + "/" + configName;
432
saveConfig(configPath);
434
/* Try force GC to avoid late destroy of PJ objects as they should be
435
* deleted before lib is destroyed.
437
Runtime.getRuntime().gc();
439
/* Shutdown pjsua. Note that Endpoint destructor will also invoke
440
* libDestroy(), so this will be a test of double libDestroy().
444
} catch (Exception e) {}
446
/* Force delete Endpoint here, to avoid deletion from a non-
447
* registered thread (by GC?).