blob: 0a49e4eabc6ec3d5fb5c992f3c9b763b8eccd4e0 [file] [log] [blame]
Emeric Vigier6119d782012-09-21 18:04:14 -04001/**
2 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
3 * Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
4 *
5 * Author: Regis Montoya <r3gis.3R@gmail.com>
6 * Author: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 * If you own a pjsip commercial license you can also redistribute it
13 * and/or modify it under the terms of the GNU Lesser General Public License
14 * as an android library.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
Emeric Vigiereaf2c492012-09-19 14:38:20 -040024package com.savoirfairelinux.sflphone.service;
25
Emeric Vigier6119d782012-09-21 18:04:14 -040026import java.lang.ref.WeakReference;
27
Emeric Vigiereaf2c492012-09-19 14:38:20 -040028import android.app.Service;
29import android.content.Intent;
30import android.os.Binder;
Emeric Vigier6119d782012-09-21 18:04:14 -040031import android.os.Handler;
32import android.os.HandlerThread;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040033import android.os.IBinder;
Emeric Vigier6119d782012-09-21 18:04:14 -040034import android.os.Looper;
35import android.os.Message;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040036import android.util.Log;
37import android.widget.Toast;
38
Emeric Vigier0007dee2012-09-24 11:35:58 -040039import com.savoirfairelinux.sflphone.service.ManagerImpl;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040040import com.savoirfairelinux.sflphone.client.SFLphoneApplication;
Emeric Vigier6119d782012-09-21 18:04:14 -040041import com.savoirfairelinux.sflphone.service.ISipService;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040042
Alexandre Savard713a34d2012-09-26 15:50:41 -040043import java.util.HashMap;
Alexandre Savard6b85e7e2012-09-27 15:43:14 -040044import java.util.ArrayList;
Alexandre Savard713a34d2012-09-26 15:50:41 -040045
Emeric Vigiereaf2c492012-09-19 14:38:20 -040046public class SipService extends Service {
47
48 static final String TAG = "SipService";
49 static final int DELAY = 5000; /* 5 sec */
50 private boolean runFlag = false;
51 private SipServiceThread sipServiceThread;
Emeric Vigier84e05da2012-09-20 14:53:05 -040052 private SFLphoneApplication sflphoneApp;
Emeric Vigier6119d782012-09-21 18:04:14 -040053 private SipServiceExecutor mExecutor;
54 private static HandlerThread executorThread;
55 private CallManagerJNI callManagerJNI;
Emeric Vigier0007dee2012-09-24 11:35:58 -040056 private CallManagerCallBack callManagerCallBack;
Alexandre Savardc1b08fe2012-09-25 16:24:47 -040057 private ConfigurationManagerJNI configurationManagerJNI;
Emeric Vigier0007dee2012-09-24 11:35:58 -040058 private ManagerImpl managerImpl;
Emeric Vigier6119d782012-09-21 18:04:14 -040059 private boolean isPjSipStackStarted = false;
60
61 /* Implement public interface for the service */
62 private final ISipService.Stub mBinder = new ISipService.Stub() {
63
64 @Override
65 public void placeCall(final String accountID, final String callID, final String to) {
66 getExecutor().execute(new SipRunnable() {
67 @Override
68 protected void doRun() throws SameThreadException {
69 Log.i(TAG, "SipService.placeCall() thread running...");
70 callManagerJNI.placeCall(accountID, callID, to);
71 }
72 });
73 }
74
75 @Override
76 public void refuse(final String callID) {
77 getExecutor().execute(new SipRunnable() {
78 @Override
79 protected void doRun() throws SameThreadException {
80 Log.i(TAG, "SipService.refuse() thread running...");
81 callManagerJNI.refuse(callID);
82 }
83 });
84 }
85
86 @Override
87 public void accept(final String callID) {
88 getExecutor().execute(new SipRunnable() {
89 @Override
90 protected void doRun() throws SameThreadException {
91 Log.i(TAG, "SipService.placeCall() thread running...");
92 callManagerJNI.accept(callID);
93 }
94 });
95 }
96
97 @Override
98 public void hangUp(final String callID) {
99 getExecutor().execute(new SipRunnable() {
100 @Override
101 protected void doRun() throws SameThreadException {
102 Log.i(TAG, "SipService.hangUp() thread running...");
103 callManagerJNI.hangUp(callID);
104 }
105 });
106 }
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400107
108 @Override
109 public void setAudioPlugin(final String audioPlugin) {
110 getExecutor().execute(new SipRunnable() {
111 @Override
112 protected void doRun() throws SameThreadException {
113 Log.i(TAG, "SipService.setAudioPlugin() thread running...");
114 configurationManagerJNI.setAudioPlugin(audioPlugin);
115 }
116 });
117 }
Alexandre Savard713a34d2012-09-26 15:50:41 -0400118
119 @Override
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400120 public ArrayList<String> getAccountList() {
121 StringVect swigvect = configurationManagerJNI.getAccountList();
122 ArrayList<String> nativelist = new ArrayList<String>();
Alexandre Savard52a72522012-09-27 16:40:13 -0400123
124 for(int i = 0; i < swigvect.size(); i++)
125 nativelist.add(swigvect.get(i));
126
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400127 return nativelist;
128 }
129
130 @Override
Alexandre Savard713a34d2012-09-26 15:50:41 -0400131 public HashMap<String,String> getAccountDetails(final String accountID) {
132 StringMap swigmap = configurationManagerJNI.getAccountDetails(accountID);
133
134 HashMap<String, String> nativemap = new HashMap<String, String>();
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400135
136 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_ALIAS, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_ALIAS));
137 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_HOSTNAME, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_HOSTNAME));
138 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_USERNAME, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_USERNAME));
139 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_ROUTESET, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_ROUTESET));
140 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_EXPIRE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_EXPIRE));
141 nativemap.put(ServiceConstants.CONFIG_LOCAL_INTERFACE, swigmap.get(ServiceConstants.CONFIG_LOCAL_INTERFACE));
142 nativemap.put(ServiceConstants.CONFIG_STUN_SERVER, swigmap.get(ServiceConstants.CONFIG_STUN_SERVER));
143 nativemap.put(ServiceConstants.CONFIG_TLS_ENABLE, swigmap.get(ServiceConstants.CONFIG_TLS_ENABLE));
144 nativemap.put(ServiceConstants.CONFIG_SRTP_ENABLE, swigmap.get(ServiceConstants.CONFIG_SRTP_ENABLE));
145
146 /*
147 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_TYPE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_TYPE));
148 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_ALIAS, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_ALIAS));
149 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_MAILBOX, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_MAILBOX));
150 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_ENABLE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_ENABLE));
151 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_EXPIRE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_EXPIRE));
152 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATUS, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATUS));
153 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATE_CODE));
154 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATE_DESC, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REGISTRATION_STATE_DESC));
155 nativemap.put(ServiceConstants.CONFIG_CREDENTIAL_NUMBER, swigmap.get(ServiceConstants.CONFIG_CREDENTIAL_NUMBER));
156 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_DTMF_TYPE, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_DTMF_TYPE));
157 nativemap.put(ServiceConstants.CONFIG_RINGTONE_PATH, swigmap.get(ServiceConstants.CONFIG_RINGTONE_PATH));
158 nativemap.put(ServiceConstants.CONFIG_RINGTONE_ENABLED, swigmap.get(ServiceConstants.CONFIG_RINGTONE_ENABLED));
159 nativemap.put(ServiceConstants.CONFIG_KEEP_ALIVE_ENABLED, swigmap.get(ServiceConstants.CONFIG_KEEP_ALIVE_ENABLED));
160 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_PASSWORD, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_PASSWORD));
161 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_REALM, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_REALM));
162 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_DEFAULT_REALM, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_DEFAULT_REALM));
163 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_USERAGENT, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_USERAGENT));
164 nativemap.put(ServiceConstants.CONFIG_ACCOUNT_AUTOANSWER, swigmap.get(ServiceConstants.CONFIG_ACCOUNT_AUTOANSWER));
165 nativemap.put(ServiceConstants.CONFIG_INTERFACE, swigmap.get(ServiceConstants.CONFIG_INTERFACE));
166 nativemap.put(ServiceConstants.CONFIG_PUBLISHED_SAMEAS_LOCAL, swigmap.get(ServiceConstants.CONFIG_PUBLISHED_SAMEAS_LOCAL));
167 nativemap.put(ServiceConstants.CONFIG_DEFAULT_INTERFACE, swigmap.get(ServiceConstants.CONFIG_DEFAULT_INTERFACE));
168 nativemap.put(ServiceConstants.CONFIG_DISPLAY_NAME, swigmap.get(ServiceConstants.CONFIG_DISPLAY_NAME));
169 nativemap.put(ServiceConstants.CONFIG_DEFAULT_ADDRESS, swigmap.get(ServiceConstants.CONFIG_DEFAULT_ADDRESS));
170 nativemap.put(ServiceConstants.CONFIG_SRTP_KEY_EXCHANGE, swigmap.get(ServiceConstants.CONFIG_SRTP_KEY_EXCHANGE));
171 nativemap.put(ServiceConstants.CONFIG_SRTP_ENCRYPTION_ALGO, swigmap.get(ServiceConstants.CONFIG_SRTP_ENCRYPTION_ALGO));
172 nativemap.put(ServiceConstants.CONFIG_SRTP_RTP_FALLBACK, swigmap.get(ServiceConstants.CONFIG_SRTP_RTP_FALLBACK));
173 nativemap.put(ServiceConstants.CONFIG_ZRTP_HELLO_HASH, swigmap.get(ServiceConstants.CONFIG_ZRTP_HELLO_HASH));
174 nativemap.put(ServiceConstants.CONFIG_ZRTP_DISPLAY_SAS, swigmap.get(ServiceConstants.CONFIG_ZRTP_DISPLAY_SAS));
175 nativemap.put(ServiceConstants.CONFIG_ZRTP_NOT_SUPP_WARNING, swigmap.get(ServiceConstants.CONFIG_ZRTP_NOT_SUPP_WARNING));
176 nativemap.put(ServiceConstants.CONFIG_ZRTP_DISPLAY_SAS_ONCE, swigmap.get(ServiceConstants.CONFIG_ZRTP_DISPLAY_SAS_ONCE));
177 nativemap.put(ServiceConstants.CONFIG_TLS_LISTENER_PORT, swigmap.get(ServiceConstants.CONFIG_TLS_LISTENER_PORT));
178 nativemap.put(ServiceConstants.CONFIG_TLS_CA_LIST_FILE, swigmap.get(ServiceConstants.CONFIG_TLS_CA_LIST_FILE));
179 nativemap.put(ServiceConstants.CONFIG_TLS_CERTIFICATE_FILE, swigmap.get(ServiceConstants.CONFIG_TLS_CERTIFICATE_FILE));
180 nativemap.put(ServiceConstants.CONFIG_TLS_PRIVATE_KEY_FILE, swigmap.get(ServiceConstants.CONFIG_TLS_PRIVATE_KEY_FILE));
181 nativemap.put(ServiceConstants.CONFIG_TLS_PASSWORD, swigmap.get(ServiceConstants.CONFIG_TLS_PASSWORD));
182 nativemap.put(ServiceConstants.CONFIG_TLS_METHOD, swigmap.get(ServiceConstants.CONFIG_TLS_METHOD));
183 nativemap.put(ServiceConstants.CONFIG_TLS_CIPHERS, swigmap.get(ServiceConstants.CONFIG_TLS_CIPHERS));
184 nativemap.put(ServiceConstants.CONFIG_TLS_SERVER_NAME, swigmap.get(ServiceConstants.CONFIG_TLS_SERVER_NAME));
185 nativemap.put(ServiceConstants.CONFIG_TLS_VERIFY_SERVER, swigmap.get(ServiceConstants.CONFIG_TLS_VERIFY_SERVER));
186 nativemap.put(ServiceConstants.CONFIG_TLS_VERIFY_CLIENT, swigmap.get(ServiceConstants.CONFIG_TLS_VERIFY_CLIENT));
187 nativemap.put(ServiceConstants.CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, swigmap.get(ServiceConstants.CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE));
188 nativemap.put(ServiceConstants.CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, swigmap.get(ServiceConstants.CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC));
189 nativemap.put(ServiceConstants.CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC, swigmap.get(ServiceConstants.CONFIG_TLS_NEGOTIATION_TIMEOUT_MSEC));
190 */
Alexandre Savard713a34d2012-09-26 15:50:41 -0400191
192 return nativemap;
193 }
Emeric Vigier6119d782012-09-21 18:04:14 -0400194 };
195
196 /**
197 * Class used for the client Binder. Because we know this service always
198 * runs in the same process as its clients, we don't need to deal with IPC.
199 */
200 public class LocalBinder extends Binder {
201 public SipService getService() {
202 // Return this instance of LocalService so clients can call public methods
203 return SipService.this;
204 }
205 }
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400206
207 /* called once by startService() */
208 @Override
209 public void onCreate() {
210 Log.i(TAG, "onCreated");
211 super.onCreate();
Emeric Vigier6119d782012-09-21 18:04:14 -0400212 sflphoneApp = (SFLphoneApplication) getApplication();
213 sipServiceThread = new SipServiceThread();
214 getExecutor().execute(new StartRunnable());
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400215 }
216
217 /* called for each startService() */
218 @Override
219 public int onStartCommand(Intent intent, int flags, int startId) {
220 Log.i(TAG, "onStarted");
221 super.onStartCommand(intent, flags, startId);
Emeric Vigier6119d782012-09-21 18:04:14 -0400222
223 runFlag = true;
224 sipServiceThread.start();
225 sflphoneApp.setServiceRunning(true);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400226 Toast.makeText(this, "Sflphone Service started", Toast.LENGTH_SHORT).show();
Emeric Vigier6119d782012-09-21 18:04:14 -0400227
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400228 Log.i(TAG, "onStarted");
229 return START_STICKY; /* started and stopped explicitly */
230 }
231
232 @Override
233 public void onDestroy() {
234 /* called once by stopService() */
235 super.onDestroy();
Emeric Vigier6119d782012-09-21 18:04:14 -0400236 runFlag = false;
237 sipServiceThread.interrupt();
238 sipServiceThread = null;
239 sflphoneApp.setServiceRunning(false);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400240 Toast.makeText(this, "Sflphone Service stopped", Toast.LENGTH_SHORT).show();
241
242 Log.i(TAG, "onDestroyed");
243 }
244
245 @Override
246 public IBinder onBind(Intent arg0) {
247 Log.i(TAG, "onBound");
248 return mBinder;
249 }
250
Emeric Vigier6119d782012-09-21 18:04:14 -0400251 private static Looper createLooper() {
252 if(executorThread == null) {
253 Log.d(TAG, "Creating new handler thread");
254 // ADT gives a fake warning due to bad parse rule.
255 executorThread = new HandlerThread("SipService.Executor");
256 executorThread.start();
257 }
258 return executorThread.getLooper();
259 }
260
261 public SipServiceExecutor getExecutor() {
262 // create mExecutor lazily
263 if (mExecutor == null) {
264 mExecutor = new SipServiceExecutor(this);
265 }
266 return mExecutor;
267 }
268
269 // Executes immediate tasks in a single executorThread.
270 public static class SipServiceExecutor extends Handler {
271 WeakReference<SipService> handlerService;
272
273 SipServiceExecutor(SipService s) {
274 super(createLooper());
275 handlerService = new WeakReference<SipService>(s);
276 }
277
278 public void execute(Runnable task) {
279 // TODO: add wakelock
280 Message.obtain(this, 0/* don't care */, task).sendToTarget();
281 }
282
283 @Override
284 public void handleMessage(Message msg) {
285 if (msg.obj instanceof Runnable) {
286 executeInternal((Runnable) msg.obj);
287 } else {
288 Log.w(TAG, "can't handle msg: " + msg);
289 }
290 }
291
292 private void executeInternal(Runnable task) {
293 try {
294 task.run();
295 } catch (Throwable t) {
296 Log.e(TAG, "run task: " + task, t);
297 }
298 }
299 }
300
301 private void startPjSipStack() throws SameThreadException {
302 if (isPjSipStackStarted)
303 return;
304
305 try {
306 System.loadLibrary("gnustl_shared");
307 System.loadLibrary("expat");
308 System.loadLibrary("yaml");
309 System.loadLibrary("ccgnu2");
310 System.loadLibrary("crypto");
311 System.loadLibrary("ssl");
312 System.loadLibrary("ccrtp1");
313 System.loadLibrary("dbus");
314 System.loadLibrary("dbus-c++-1");
315 System.loadLibrary("samplerate");
316 System.loadLibrary("codec_ulaw");
317 System.loadLibrary("codec_alaw");
318 System.loadLibrary("speexresampler");
319 System.loadLibrary("sflphone");
320 isPjSipStackStarted = true;
321 } catch (UnsatisfiedLinkError e) {
322 Log.e(TAG, "Problem with the current Pj stack...", e);
323 isPjSipStackStarted = false;
324 return;
325 } catch (Exception e) {
326 Log.e(TAG, "Problem with the current Pj stack...", e);
327 }
328
Emeric Vigier0007dee2012-09-24 11:35:58 -0400329 /* get unique instance of managerImpl */
330 managerImpl = SFLPhoneservice.instance();
331 Log.i(TAG, "ManagerImpl::instance() = " + managerImpl);
Emeric Vigier6119d782012-09-21 18:04:14 -0400332 /* set static AppPath before calling manager.init */
Emeric Vigier0007dee2012-09-24 11:35:58 -0400333 managerImpl.setPath(sflphoneApp.getAppPath());
334 callManagerJNI = new CallManagerJNI();
Emeric Vigier6119d782012-09-21 18:04:14 -0400335 Log.i(TAG, "startPjSipStack() callManagerJNI = " + callManagerJNI);
336
Emeric Vigier0007dee2012-09-24 11:35:58 -0400337 callManagerCallBack = new CallManagerCallBack();
338 SFLPhoneservice.setCallbackObject(callManagerCallBack);
339 Log.i(TAG, "callManagerCallBack = " + callManagerCallBack);
340
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400341 configurationManagerJNI = new ConfigurationManagerJNI();
342
Emeric Vigier0007dee2012-09-24 11:35:58 -0400343 managerImpl.init("");
Emeric Vigier6119d782012-09-21 18:04:14 -0400344 return;
345 }
346
347 // Enforce same thread contract to ensure we do not call from somewhere else
348 public class SameThreadException extends Exception {
349 private static final long serialVersionUID = -905639124232613768L;
350
351 public SameThreadException() {
352 super("Should be launched from a single worker thread");
353 }
354 }
355
356 public abstract static class SipRunnable implements Runnable {
357 protected abstract void doRun() throws SameThreadException;
358
359 public void run() {
360 try {
361 doRun();
362 }catch(SameThreadException e) {
363 Log.e(TAG, "Not done from same thread");
364 }
365 }
366 }
367
368 class StartRunnable extends SipRunnable {
369 @Override
370 protected void doRun() throws SameThreadException {
371 startPjSipStack();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400372 }
373 }
374
375 private class SipServiceThread extends Thread {
376
377 public SipServiceThread() {
378 super("sipServiceThread");
379 }
380
381 @Override
382 public void run() {
Emeric Vigier12d61d82012-09-19 15:08:18 -0400383 Log.i(TAG, "SipService thread running...");
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400384 SipService sipService = SipService.this;
385 while(sipService.runFlag) {
386 try {
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400387 Thread.sleep(DELAY);
388 } catch (InterruptedException e) {
389 sipService.runFlag = false;
390 Log.w(TAG, "service thread interrupted!");
391 }
392 }
393 }
394 }
395}