blob: ec1f0b6d127b6e0049cbc1ee4528834244dbbbd0 [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;
alision17052d42013-04-22 10:39:38 -040027import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.Map;
Emeric Vigier6119d782012-09-21 18:04:14 -040030
Emeric Vigiereaf2c492012-09-19 14:38:20 -040031import android.app.Service;
alision17052d42013-04-22 10:39:38 -040032import android.content.BroadcastReceiver;
33import android.content.Context;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040034import android.content.Intent;
alision17052d42013-04-22 10:39:38 -040035import android.content.IntentFilter;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040036import android.os.Binder;
Emeric Vigier6119d782012-09-21 18:04:14 -040037import android.os.Handler;
38import android.os.HandlerThread;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040039import android.os.IBinder;
Emeric Vigier6119d782012-09-21 18:04:14 -040040import android.os.Looper;
41import android.os.Message;
alision17052d42013-04-22 10:39:38 -040042import android.os.Vibrator;
43import android.support.v4.content.LocalBroadcastManager;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040044import android.util.Log;
45import android.widget.Toast;
46
alisionf76de3b2013-04-16 15:35:22 -040047import com.savoirfairelinux.sflphone.account.AccountDetailsHandler;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040048import com.savoirfairelinux.sflphone.client.SFLphoneApplication;
alision17052d42013-04-22 10:39:38 -040049import com.savoirfairelinux.sflphone.client.receiver.CallListReceiver;
Alexandre Savard713a34d2012-09-26 15:50:41 -040050
Emeric Vigiereaf2c492012-09-19 14:38:20 -040051public class SipService extends Service {
52
53 static final String TAG = "SipService";
54 static final int DELAY = 5000; /* 5 sec */
55 private boolean runFlag = false;
56 private SipServiceThread sipServiceThread;
Emeric Vigier84e05da2012-09-20 14:53:05 -040057 private SFLphoneApplication sflphoneApp;
Emeric Vigier6119d782012-09-21 18:04:14 -040058 private SipServiceExecutor mExecutor;
59 private static HandlerThread executorThread;
60 private CallManagerJNI callManagerJNI;
Emeric Vigier0007dee2012-09-24 11:35:58 -040061 private CallManagerCallBack callManagerCallBack;
Alexandre Savardc1b08fe2012-09-25 16:24:47 -040062 private ConfigurationManagerJNI configurationManagerJNI;
Alexandre Savardfccd1dc2012-10-17 17:31:38 -040063 private ConfigurationManagerCallback configurationManagerCallback;
Emeric Vigier0007dee2012-09-24 11:35:58 -040064 private ManagerImpl managerImpl;
Emeric Vigier6119d782012-09-21 18:04:14 -040065 private boolean isPjSipStackStarted = false;
alision17052d42013-04-22 10:39:38 -040066 public CallListReceiver mCallList;
Alexandre Savard4f11d7a2012-10-18 13:15:36 -040067
Emeric Vigier6119d782012-09-21 18:04:14 -040068
69 /* Implement public interface for the service */
70 private final ISipService.Stub mBinder = new ISipService.Stub() {
71
72 @Override
73 public void placeCall(final String accountID, final String callID, final String to) {
74 getExecutor().execute(new SipRunnable() {
75 @Override
76 protected void doRun() throws SameThreadException {
77 Log.i(TAG, "SipService.placeCall() thread running...");
78 callManagerJNI.placeCall(accountID, callID, to);
79 }
80 });
81 }
82
83 @Override
84 public void refuse(final String callID) {
85 getExecutor().execute(new SipRunnable() {
86 @Override
87 protected void doRun() throws SameThreadException {
88 Log.i(TAG, "SipService.refuse() thread running...");
89 callManagerJNI.refuse(callID);
90 }
91 });
92 }
93
94 @Override
95 public void accept(final String callID) {
96 getExecutor().execute(new SipRunnable() {
97 @Override
98 protected void doRun() throws SameThreadException {
99 Log.i(TAG, "SipService.placeCall() thread running...");
100 callManagerJNI.accept(callID);
101 }
102 });
103 }
104
105 @Override
106 public void hangUp(final String callID) {
107 getExecutor().execute(new SipRunnable() {
108 @Override
109 protected void doRun() throws SameThreadException {
110 Log.i(TAG, "SipService.hangUp() thread running...");
111 callManagerJNI.hangUp(callID);
112 }
113 });
114 }
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400115
116 @Override
Alexandre Savarde9dc8992012-10-26 12:12:27 -0400117 public void hold(final String callID) {
118 getExecutor().execute(new SipRunnable() {
119 @Override
120 protected void doRun() throws SameThreadException {
121 Log.i(TAG, "SipService.hold() thread running...");
122 callManagerJNI.hold(callID);
123 }
124 });
125 }
126
127 @Override
128 public void unhold(final String callID) {
129 getExecutor().execute(new SipRunnable() {
130 @Override
131 protected void doRun() throws SameThreadException {
132 Log.i(TAG, "SipService.unhold() thread running...");
133 callManagerJNI.unhold(callID);
134 }
135 });
136 }
137
138 @Override
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400139 public void setAudioPlugin(final String audioPlugin) {
140 getExecutor().execute(new SipRunnable() {
141 @Override
142 protected void doRun() throws SameThreadException {
143 Log.i(TAG, "SipService.setAudioPlugin() thread running...");
144 configurationManagerJNI.setAudioPlugin(audioPlugin);
145 }
Alexandre Savard31d27c62012-10-04 16:05:08 -0400146 });
147 }
148
149 @Override
150 public String getCurrentAudioOutputPlugin() {
151 class CurrentAudioPlugin extends SipRunnableWithReturn {
152 @Override
153 protected String doRun() throws SameThreadException {
154 Log.i(TAG, "SipService.getCurrentAudioOutputPlugin() thread running...");
155 return configurationManagerJNI.getCurrentAudioOutputPlugin();
156 }
157 };
158
159 CurrentAudioPlugin runInstance = new CurrentAudioPlugin();
160 getExecutor().execute(runInstance);
161 while(!runInstance.isDone()) {}
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400162 return (String) runInstance.getVal();
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400163 }
Alexandre Savard713a34d2012-09-26 15:50:41 -0400164
165 @Override
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400166 public ArrayList<String> getAccountList() {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400167 class AccountList extends SipRunnableWithReturn {
168 @Override
169 protected StringVect doRun() throws SameThreadException {
170 Log.i(TAG, "SipService.getAccountList() thread running...");
171 return configurationManagerJNI.getAccountList();
172 }
173 };
174 AccountList runInstance = new AccountList();
175 getExecutor().execute(runInstance);
176 while(!runInstance.isDone()) {}
177 StringVect swigvect = (StringVect) runInstance.getVal();
178
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400179 ArrayList<String> nativelist = new ArrayList<String>();
Alexandre Savard52a72522012-09-27 16:40:13 -0400180
181 for(int i = 0; i < swigvect.size(); i++)
182 nativelist.add(swigvect.get(i));
183
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400184 return nativelist;
185 }
186
187 @Override
Alexandre Savard713a34d2012-09-26 15:50:41 -0400188 public HashMap<String,String> getAccountDetails(final String accountID) {
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400189 class AccountDetails extends SipRunnableWithReturn {
190 private String id;
191 AccountDetails(String accountId) { id = accountId; }
192 @Override
193 protected StringMap doRun() throws SameThreadException {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400194 Log.i(TAG, "SipService.getAccountDetails() thread running...");
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400195 return configurationManagerJNI.getAccountDetails(id);
196 }
197 };
198
199 AccountDetails runInstance = new AccountDetails(accountID);
200 getExecutor().execute(runInstance);
201 while(!runInstance.isDone()) {}
202 StringMap swigmap = (StringMap) runInstance.getVal();
Alexandre Savard713a34d2012-09-26 15:50:41 -0400203
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400204 HashMap<String, String> nativemap = AccountDetailsHandler.convertSwigToNative(swigmap);
Alexandre Savard713a34d2012-09-26 15:50:41 -0400205
206 return nativemap;
207 }
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400208
209 @Override
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400210 public void setAccountDetails(final String accountId, Map map) {
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400211 HashMap<String,String> nativemap = (HashMap<String,String>) map;
212
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400213 final StringMap swigmap = AccountDetailsHandler.convertFromNativeToSwig(nativemap);
Alexandre Savard718d49f2012-10-02 15:17:13 -0400214
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400215 getExecutor().execute(new SipRunnable() {
216 @Override
217 protected void doRun() throws SameThreadException {
218 Log.i(TAG, "SipService.setAccountDetails() thread running...");
219 configurationManagerJNI.setAccountDetails(accountId, swigmap);
220 }
221 });
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400222 }
223
Alexandre Savard46036572012-10-05 13:56:49 -0400224 @Override
225 public String addAccount(Map map) {
226 class AddAccount extends SipRunnableWithReturn {
227 StringMap map;
228 AddAccount(StringMap m) { map = m; }
229 @Override
230 protected String doRun() throws SameThreadException {
231 Log.i(TAG, "SipService.getAccountDetails() thread running...");
232 return configurationManagerJNI.addAccount(map);
233 }
234 };
235
236 final StringMap swigmap = AccountDetailsHandler.convertFromNativeToSwig((HashMap<String,String>)map);
237
238 AddAccount runInstance = new AddAccount(swigmap);
239 getExecutor().execute(runInstance);
240 while(!runInstance.isDone()) {}
241 String accountId = (String) runInstance.getVal();
242
243 return accountId;
244 }
245
246 @Override
247 public void removeAccount(final String accountId) {
248 getExecutor().execute(new SipRunnable() {
249 @Override
250 protected void doRun() throws SameThreadException {
251 Log.i(TAG, "SipService.setAccountDetails() thread running...");
252 configurationManagerJNI.removeAccount(accountId);
253 }
254 });
255 }
Emeric Vigier6119d782012-09-21 18:04:14 -0400256 };
alision17052d42013-04-22 10:39:38 -0400257 private BroadcastReceiver IncomingReceiver = new BroadcastReceiver() {
258
259 @Override
260 public void onReceive(Context context, Intent intent) {
261 Log.i(TAG, "Received"+ intent.getAction());
262 // Get instance of Vibrator from current Context
263 Vibrator mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
264 mVibrator.vibrate(300);
265 }
266 };
Emeric Vigier6119d782012-09-21 18:04:14 -0400267
268 /**
269 * Class used for the client Binder. Because we know this service always
270 * runs in the same process as its clients, we don't need to deal with IPC.
271 */
272 public class LocalBinder extends Binder {
273 public SipService getService() {
274 // Return this instance of LocalService so clients can call public methods
275 return SipService.this;
276 }
277 }
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400278
279 /* called once by startService() */
280 @Override
281 public void onCreate() {
282 Log.i(TAG, "onCreated");
283 super.onCreate();
Emeric Vigier6119d782012-09-21 18:04:14 -0400284 sflphoneApp = (SFLphoneApplication) getApplication();
285 sipServiceThread = new SipServiceThread();
alision17052d42013-04-22 10:39:38 -0400286 mCallList = new CallListReceiver();
287
288 IntentFilter callFilter = new IntentFilter(CallManagerCallBack.NEW_CALL_CREATED);
289 callFilter.addAction(CallManagerCallBack.INCOMING_CALL);
290 LocalBroadcastManager.getInstance(this).registerReceiver(IncomingReceiver , callFilter);
Emeric Vigier6119d782012-09-21 18:04:14 -0400291 getExecutor().execute(new StartRunnable());
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400292 }
293
294 /* called for each startService() */
295 @Override
296 public int onStartCommand(Intent intent, int flags, int startId) {
297 Log.i(TAG, "onStarted");
298 super.onStartCommand(intent, flags, startId);
Emeric Vigier6119d782012-09-21 18:04:14 -0400299
Emeric Vigier13179522012-11-06 17:44:21 -0500300 if (!runFlag) {
301 sipServiceThread.start();
302 runFlag = true;
303 sflphoneApp.setServiceRunning(true);
304 Toast.makeText(this, "Sflphone Service started", Toast.LENGTH_SHORT).show();
305 }
Emeric Vigier6119d782012-09-21 18:04:14 -0400306
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400307 return START_STICKY; /* started and stopped explicitly */
308 }
309
310 @Override
311 public void onDestroy() {
312 /* called once by stopService() */
Emeric Vigier6119d782012-09-21 18:04:14 -0400313 sipServiceThread.interrupt();
314 sipServiceThread = null;
Emeric Vigier13179522012-11-06 17:44:21 -0500315 runFlag = false;
Emeric Vigier6119d782012-09-21 18:04:14 -0400316 sflphoneApp.setServiceRunning(false);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400317 Toast.makeText(this, "Sflphone Service stopped", Toast.LENGTH_SHORT).show();
Emeric Vigier13179522012-11-06 17:44:21 -0500318 super.onDestroy();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400319
320 Log.i(TAG, "onDestroyed");
321 }
322
323 @Override
324 public IBinder onBind(Intent arg0) {
325 Log.i(TAG, "onBound");
326 return mBinder;
327 }
328
Emeric Vigier6119d782012-09-21 18:04:14 -0400329 private static Looper createLooper() {
330 if(executorThread == null) {
331 Log.d(TAG, "Creating new handler thread");
332 // ADT gives a fake warning due to bad parse rule.
333 executorThread = new HandlerThread("SipService.Executor");
334 executorThread.start();
335 }
336 return executorThread.getLooper();
337 }
338
339 public SipServiceExecutor getExecutor() {
340 // create mExecutor lazily
341 if (mExecutor == null) {
342 mExecutor = new SipServiceExecutor(this);
343 }
344 return mExecutor;
345 }
346
347 // Executes immediate tasks in a single executorThread.
348 public static class SipServiceExecutor extends Handler {
349 WeakReference<SipService> handlerService;
350
351 SipServiceExecutor(SipService s) {
352 super(createLooper());
353 handlerService = new WeakReference<SipService>(s);
354 }
355
356 public void execute(Runnable task) {
357 // TODO: add wakelock
358 Message.obtain(this, 0/* don't care */, task).sendToTarget();
359 }
360
361 @Override
362 public void handleMessage(Message msg) {
363 if (msg.obj instanceof Runnable) {
364 executeInternal((Runnable) msg.obj);
365 } else {
366 Log.w(TAG, "can't handle msg: " + msg);
367 }
368 }
369
370 private void executeInternal(Runnable task) {
371 try {
372 task.run();
373 } catch (Throwable t) {
374 Log.e(TAG, "run task: " + task, t);
375 }
376 }
377 }
378
379 private void startPjSipStack() throws SameThreadException {
380 if (isPjSipStackStarted)
381 return;
382
383 try {
384 System.loadLibrary("gnustl_shared");
385 System.loadLibrary("expat");
386 System.loadLibrary("yaml");
387 System.loadLibrary("ccgnu2");
388 System.loadLibrary("crypto");
389 System.loadLibrary("ssl");
390 System.loadLibrary("ccrtp1");
391 System.loadLibrary("dbus");
392 System.loadLibrary("dbus-c++-1");
393 System.loadLibrary("samplerate");
394 System.loadLibrary("codec_ulaw");
395 System.loadLibrary("codec_alaw");
396 System.loadLibrary("speexresampler");
397 System.loadLibrary("sflphone");
398 isPjSipStackStarted = true;
399 } catch (UnsatisfiedLinkError e) {
400 Log.e(TAG, "Problem with the current Pj stack...", e);
401 isPjSipStackStarted = false;
402 return;
403 } catch (Exception e) {
404 Log.e(TAG, "Problem with the current Pj stack...", e);
405 }
406
Emeric Vigier0007dee2012-09-24 11:35:58 -0400407 /* get unique instance of managerImpl */
408 managerImpl = SFLPhoneservice.instance();
Alexandre Savard718d49f2012-10-02 15:17:13 -0400409
Emeric Vigier6119d782012-09-21 18:04:14 -0400410 /* set static AppPath before calling manager.init */
Emeric Vigier0007dee2012-09-24 11:35:58 -0400411 managerImpl.setPath(sflphoneApp.getAppPath());
Emeric Vigier6119d782012-09-21 18:04:14 -0400412
Alexandre Savard718d49f2012-10-02 15:17:13 -0400413 callManagerJNI = new CallManagerJNI();
Alexandre Savard74c1cad2012-10-24 16:39:00 -0400414 callManagerCallBack = new CallManagerCallBack(this);
Emeric Vigier0007dee2012-09-24 11:35:58 -0400415 SFLPhoneservice.setCallbackObject(callManagerCallBack);
Emeric Vigier0007dee2012-09-24 11:35:58 -0400416
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400417 configurationManagerJNI = new ConfigurationManagerJNI();
Alexandre Savard4f11d7a2012-10-18 13:15:36 -0400418 configurationManagerCallback = new ConfigurationManagerCallback(this);
Alexandre Savardfccd1dc2012-10-17 17:31:38 -0400419 SFLPhoneservice.setConfigurationCallbackObject(configurationManagerCallback);
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400420
Emeric Vigier0007dee2012-09-24 11:35:58 -0400421 managerImpl.init("");
Emeric Vigier6119d782012-09-21 18:04:14 -0400422 return;
423 }
424
425 // Enforce same thread contract to ensure we do not call from somewhere else
426 public class SameThreadException extends Exception {
427 private static final long serialVersionUID = -905639124232613768L;
428
429 public SameThreadException() {
430 super("Should be launched from a single worker thread");
431 }
432 }
433
434 public abstract static class SipRunnable implements Runnable {
435 protected abstract void doRun() throws SameThreadException;
436
437 public void run() {
438 try {
439 doRun();
440 }catch(SameThreadException e) {
441 Log.e(TAG, "Not done from same thread");
442 }
443 }
444 }
445
Alexandre Savard31d27c62012-10-04 16:05:08 -0400446 public abstract static class SipRunnableWithReturn implements Runnable {
447 Object obj = null;
448 boolean done = false;
449
450 protected abstract Object doRun() throws SameThreadException;
451
452 public Object getVal() {
453 return obj;
454 }
455
456 public boolean isDone() {
457 return done;
458 }
459
460 public void run() {
461 try {
462 obj = doRun();
463 done = true;
464 }catch(SameThreadException e) {
465 Log.e(TAG, "Not done from same thread");
466 }
467 }
468 }
469
Emeric Vigier6119d782012-09-21 18:04:14 -0400470 class StartRunnable extends SipRunnable {
471 @Override
472 protected void doRun() throws SameThreadException {
473 startPjSipStack();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400474 }
475 }
476
477 private class SipServiceThread extends Thread {
478
479 public SipServiceThread() {
480 super("sipServiceThread");
481 }
482
483 @Override
484 public void run() {
Emeric Vigier12d61d82012-09-19 15:08:18 -0400485 Log.i(TAG, "SipService thread running...");
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400486 SipService sipService = SipService.this;
487 while(sipService.runFlag) {
488 try {
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400489 Thread.sleep(DELAY);
490 } catch (InterruptedException e) {
491 sipService.runFlag = false;
492 Log.w(TAG, "service thread interrupted!");
493 }
494 }
495 }
496 }
497}