blob: e0600065a809a8abc2b957f5cc4592bcd7d5e0f4 [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;
alisionf76de3b2013-04-16 15:35:22 -040040import com.savoirfairelinux.sflphone.account.AccountDetailsHandler;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040041import com.savoirfairelinux.sflphone.client.SFLphoneApplication;
Emeric Vigier6119d782012-09-21 18:04:14 -040042import com.savoirfairelinux.sflphone.service.ISipService;
Emeric Vigiereaf2c492012-09-19 14:38:20 -040043
Alexandre Savard8b7d4332012-09-30 20:02:11 -040044import java.util.Map;
Alexandre Savard713a34d2012-09-26 15:50:41 -040045import java.util.HashMap;
Alexandre Savard6b85e7e2012-09-27 15:43:14 -040046import java.util.ArrayList;
Alexandre Savard713a34d2012-09-26 15:50:41 -040047
Emeric Vigiereaf2c492012-09-19 14:38:20 -040048public class SipService extends Service {
49
50 static final String TAG = "SipService";
51 static final int DELAY = 5000; /* 5 sec */
52 private boolean runFlag = false;
53 private SipServiceThread sipServiceThread;
Emeric Vigier84e05da2012-09-20 14:53:05 -040054 private SFLphoneApplication sflphoneApp;
Emeric Vigier6119d782012-09-21 18:04:14 -040055 private SipServiceExecutor mExecutor;
56 private static HandlerThread executorThread;
57 private CallManagerJNI callManagerJNI;
Emeric Vigier0007dee2012-09-24 11:35:58 -040058 private CallManagerCallBack callManagerCallBack;
Alexandre Savardc1b08fe2012-09-25 16:24:47 -040059 private ConfigurationManagerJNI configurationManagerJNI;
Alexandre Savardfccd1dc2012-10-17 17:31:38 -040060 private ConfigurationManagerCallback configurationManagerCallback;
Emeric Vigier0007dee2012-09-24 11:35:58 -040061 private ManagerImpl managerImpl;
Emeric Vigier6119d782012-09-21 18:04:14 -040062 private boolean isPjSipStackStarted = false;
Alexandre Savard4f11d7a2012-10-18 13:15:36 -040063
Emeric Vigier6119d782012-09-21 18:04:14 -040064
65 /* Implement public interface for the service */
66 private final ISipService.Stub mBinder = new ISipService.Stub() {
67
68 @Override
69 public void placeCall(final String accountID, final String callID, final String to) {
70 getExecutor().execute(new SipRunnable() {
71 @Override
72 protected void doRun() throws SameThreadException {
73 Log.i(TAG, "SipService.placeCall() thread running...");
74 callManagerJNI.placeCall(accountID, callID, to);
75 }
76 });
77 }
78
79 @Override
80 public void refuse(final String callID) {
81 getExecutor().execute(new SipRunnable() {
82 @Override
83 protected void doRun() throws SameThreadException {
84 Log.i(TAG, "SipService.refuse() thread running...");
85 callManagerJNI.refuse(callID);
86 }
87 });
88 }
89
90 @Override
91 public void accept(final String callID) {
92 getExecutor().execute(new SipRunnable() {
93 @Override
94 protected void doRun() throws SameThreadException {
95 Log.i(TAG, "SipService.placeCall() thread running...");
96 callManagerJNI.accept(callID);
97 }
98 });
99 }
100
101 @Override
102 public void hangUp(final String callID) {
103 getExecutor().execute(new SipRunnable() {
104 @Override
105 protected void doRun() throws SameThreadException {
106 Log.i(TAG, "SipService.hangUp() thread running...");
107 callManagerJNI.hangUp(callID);
108 }
109 });
110 }
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400111
112 @Override
Alexandre Savarde9dc8992012-10-26 12:12:27 -0400113 public void hold(final String callID) {
114 getExecutor().execute(new SipRunnable() {
115 @Override
116 protected void doRun() throws SameThreadException {
117 Log.i(TAG, "SipService.hold() thread running...");
118 callManagerJNI.hold(callID);
119 }
120 });
121 }
122
123 @Override
124 public void unhold(final String callID) {
125 getExecutor().execute(new SipRunnable() {
126 @Override
127 protected void doRun() throws SameThreadException {
128 Log.i(TAG, "SipService.unhold() thread running...");
129 callManagerJNI.unhold(callID);
130 }
131 });
132 }
133
134 @Override
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400135 public void setAudioPlugin(final String audioPlugin) {
136 getExecutor().execute(new SipRunnable() {
137 @Override
138 protected void doRun() throws SameThreadException {
139 Log.i(TAG, "SipService.setAudioPlugin() thread running...");
140 configurationManagerJNI.setAudioPlugin(audioPlugin);
141 }
Alexandre Savard31d27c62012-10-04 16:05:08 -0400142 });
143 }
144
145 @Override
146 public String getCurrentAudioOutputPlugin() {
147 class CurrentAudioPlugin extends SipRunnableWithReturn {
148 @Override
149 protected String doRun() throws SameThreadException {
150 Log.i(TAG, "SipService.getCurrentAudioOutputPlugin() thread running...");
151 return configurationManagerJNI.getCurrentAudioOutputPlugin();
152 }
153 };
154
155 CurrentAudioPlugin runInstance = new CurrentAudioPlugin();
156 getExecutor().execute(runInstance);
157 while(!runInstance.isDone()) {}
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400158 return (String) runInstance.getVal();
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400159 }
Alexandre Savard713a34d2012-09-26 15:50:41 -0400160
161 @Override
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400162 public ArrayList<String> getAccountList() {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400163 class AccountList extends SipRunnableWithReturn {
164 @Override
165 protected StringVect doRun() throws SameThreadException {
166 Log.i(TAG, "SipService.getAccountList() thread running...");
167 return configurationManagerJNI.getAccountList();
168 }
169 };
170 AccountList runInstance = new AccountList();
171 getExecutor().execute(runInstance);
172 while(!runInstance.isDone()) {}
173 StringVect swigvect = (StringVect) runInstance.getVal();
174
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400175 ArrayList<String> nativelist = new ArrayList<String>();
Alexandre Savard52a72522012-09-27 16:40:13 -0400176
177 for(int i = 0; i < swigvect.size(); i++)
178 nativelist.add(swigvect.get(i));
179
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400180 return nativelist;
181 }
182
183 @Override
Alexandre Savard713a34d2012-09-26 15:50:41 -0400184 public HashMap<String,String> getAccountDetails(final String accountID) {
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400185 class AccountDetails extends SipRunnableWithReturn {
186 private String id;
187 AccountDetails(String accountId) { id = accountId; }
188 @Override
189 protected StringMap doRun() throws SameThreadException {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400190 Log.i(TAG, "SipService.getAccountDetails() thread running...");
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400191 return configurationManagerJNI.getAccountDetails(id);
192 }
193 };
194
195 AccountDetails runInstance = new AccountDetails(accountID);
196 getExecutor().execute(runInstance);
197 while(!runInstance.isDone()) {}
198 StringMap swigmap = (StringMap) runInstance.getVal();
Alexandre Savard713a34d2012-09-26 15:50:41 -0400199
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400200 HashMap<String, String> nativemap = AccountDetailsHandler.convertSwigToNative(swigmap);
Alexandre Savard713a34d2012-09-26 15:50:41 -0400201
202 return nativemap;
203 }
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400204
205 @Override
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400206 public void setAccountDetails(final String accountId, Map map) {
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400207 HashMap<String,String> nativemap = (HashMap<String,String>) map;
208
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400209 final StringMap swigmap = AccountDetailsHandler.convertFromNativeToSwig(nativemap);
Alexandre Savard718d49f2012-10-02 15:17:13 -0400210
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400211 getExecutor().execute(new SipRunnable() {
212 @Override
213 protected void doRun() throws SameThreadException {
214 Log.i(TAG, "SipService.setAccountDetails() thread running...");
215 configurationManagerJNI.setAccountDetails(accountId, swigmap);
216 }
217 });
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400218 }
219
Alexandre Savard46036572012-10-05 13:56:49 -0400220 @Override
221 public String addAccount(Map map) {
222 class AddAccount extends SipRunnableWithReturn {
223 StringMap map;
224 AddAccount(StringMap m) { map = m; }
225 @Override
226 protected String doRun() throws SameThreadException {
227 Log.i(TAG, "SipService.getAccountDetails() thread running...");
228 return configurationManagerJNI.addAccount(map);
229 }
230 };
231
232 final StringMap swigmap = AccountDetailsHandler.convertFromNativeToSwig((HashMap<String,String>)map);
233
234 AddAccount runInstance = new AddAccount(swigmap);
235 getExecutor().execute(runInstance);
236 while(!runInstance.isDone()) {}
237 String accountId = (String) runInstance.getVal();
238
239 return accountId;
240 }
241
242 @Override
243 public void removeAccount(final String accountId) {
244 getExecutor().execute(new SipRunnable() {
245 @Override
246 protected void doRun() throws SameThreadException {
247 Log.i(TAG, "SipService.setAccountDetails() thread running...");
248 configurationManagerJNI.removeAccount(accountId);
249 }
250 });
251 }
Emeric Vigier6119d782012-09-21 18:04:14 -0400252 };
253
254 /**
255 * Class used for the client Binder. Because we know this service always
256 * runs in the same process as its clients, we don't need to deal with IPC.
257 */
258 public class LocalBinder extends Binder {
259 public SipService getService() {
260 // Return this instance of LocalService so clients can call public methods
261 return SipService.this;
262 }
263 }
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400264
265 /* called once by startService() */
266 @Override
267 public void onCreate() {
268 Log.i(TAG, "onCreated");
269 super.onCreate();
Emeric Vigier6119d782012-09-21 18:04:14 -0400270 sflphoneApp = (SFLphoneApplication) getApplication();
271 sipServiceThread = new SipServiceThread();
272 getExecutor().execute(new StartRunnable());
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400273 }
274
275 /* called for each startService() */
276 @Override
277 public int onStartCommand(Intent intent, int flags, int startId) {
278 Log.i(TAG, "onStarted");
279 super.onStartCommand(intent, flags, startId);
Emeric Vigier6119d782012-09-21 18:04:14 -0400280
Emeric Vigier13179522012-11-06 17:44:21 -0500281 if (!runFlag) {
282 sipServiceThread.start();
283 runFlag = true;
284 sflphoneApp.setServiceRunning(true);
285 Toast.makeText(this, "Sflphone Service started", Toast.LENGTH_SHORT).show();
286 }
Emeric Vigier6119d782012-09-21 18:04:14 -0400287
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400288 return START_STICKY; /* started and stopped explicitly */
289 }
290
291 @Override
292 public void onDestroy() {
293 /* called once by stopService() */
Emeric Vigier6119d782012-09-21 18:04:14 -0400294 sipServiceThread.interrupt();
295 sipServiceThread = null;
Emeric Vigier13179522012-11-06 17:44:21 -0500296 runFlag = false;
Emeric Vigier6119d782012-09-21 18:04:14 -0400297 sflphoneApp.setServiceRunning(false);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400298 Toast.makeText(this, "Sflphone Service stopped", Toast.LENGTH_SHORT).show();
Emeric Vigier13179522012-11-06 17:44:21 -0500299 super.onDestroy();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400300
301 Log.i(TAG, "onDestroyed");
302 }
303
304 @Override
305 public IBinder onBind(Intent arg0) {
306 Log.i(TAG, "onBound");
307 return mBinder;
308 }
309
Emeric Vigier6119d782012-09-21 18:04:14 -0400310 private static Looper createLooper() {
311 if(executorThread == null) {
312 Log.d(TAG, "Creating new handler thread");
313 // ADT gives a fake warning due to bad parse rule.
314 executorThread = new HandlerThread("SipService.Executor");
315 executorThread.start();
316 }
317 return executorThread.getLooper();
318 }
319
320 public SipServiceExecutor getExecutor() {
321 // create mExecutor lazily
322 if (mExecutor == null) {
323 mExecutor = new SipServiceExecutor(this);
324 }
325 return mExecutor;
326 }
327
328 // Executes immediate tasks in a single executorThread.
329 public static class SipServiceExecutor extends Handler {
330 WeakReference<SipService> handlerService;
331
332 SipServiceExecutor(SipService s) {
333 super(createLooper());
334 handlerService = new WeakReference<SipService>(s);
335 }
336
337 public void execute(Runnable task) {
338 // TODO: add wakelock
339 Message.obtain(this, 0/* don't care */, task).sendToTarget();
340 }
341
342 @Override
343 public void handleMessage(Message msg) {
344 if (msg.obj instanceof Runnable) {
345 executeInternal((Runnable) msg.obj);
346 } else {
347 Log.w(TAG, "can't handle msg: " + msg);
348 }
349 }
350
351 private void executeInternal(Runnable task) {
352 try {
353 task.run();
354 } catch (Throwable t) {
355 Log.e(TAG, "run task: " + task, t);
356 }
357 }
358 }
359
360 private void startPjSipStack() throws SameThreadException {
361 if (isPjSipStackStarted)
362 return;
363
364 try {
365 System.loadLibrary("gnustl_shared");
366 System.loadLibrary("expat");
367 System.loadLibrary("yaml");
368 System.loadLibrary("ccgnu2");
369 System.loadLibrary("crypto");
370 System.loadLibrary("ssl");
371 System.loadLibrary("ccrtp1");
372 System.loadLibrary("dbus");
373 System.loadLibrary("dbus-c++-1");
374 System.loadLibrary("samplerate");
375 System.loadLibrary("codec_ulaw");
376 System.loadLibrary("codec_alaw");
377 System.loadLibrary("speexresampler");
378 System.loadLibrary("sflphone");
379 isPjSipStackStarted = true;
380 } catch (UnsatisfiedLinkError e) {
381 Log.e(TAG, "Problem with the current Pj stack...", e);
382 isPjSipStackStarted = false;
383 return;
384 } catch (Exception e) {
385 Log.e(TAG, "Problem with the current Pj stack...", e);
386 }
387
Emeric Vigier0007dee2012-09-24 11:35:58 -0400388 /* get unique instance of managerImpl */
389 managerImpl = SFLPhoneservice.instance();
Alexandre Savard718d49f2012-10-02 15:17:13 -0400390
Emeric Vigier6119d782012-09-21 18:04:14 -0400391 /* set static AppPath before calling manager.init */
Emeric Vigier0007dee2012-09-24 11:35:58 -0400392 managerImpl.setPath(sflphoneApp.getAppPath());
Emeric Vigier6119d782012-09-21 18:04:14 -0400393
Alexandre Savard718d49f2012-10-02 15:17:13 -0400394 callManagerJNI = new CallManagerJNI();
Alexandre Savard74c1cad2012-10-24 16:39:00 -0400395 callManagerCallBack = new CallManagerCallBack(this);
Emeric Vigier0007dee2012-09-24 11:35:58 -0400396 SFLPhoneservice.setCallbackObject(callManagerCallBack);
Emeric Vigier0007dee2012-09-24 11:35:58 -0400397
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400398 configurationManagerJNI = new ConfigurationManagerJNI();
Alexandre Savard4f11d7a2012-10-18 13:15:36 -0400399 configurationManagerCallback = new ConfigurationManagerCallback(this);
Alexandre Savardfccd1dc2012-10-17 17:31:38 -0400400 SFLPhoneservice.setConfigurationCallbackObject(configurationManagerCallback);
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400401
Emeric Vigier0007dee2012-09-24 11:35:58 -0400402 managerImpl.init("");
Emeric Vigier6119d782012-09-21 18:04:14 -0400403 return;
404 }
405
406 // Enforce same thread contract to ensure we do not call from somewhere else
407 public class SameThreadException extends Exception {
408 private static final long serialVersionUID = -905639124232613768L;
409
410 public SameThreadException() {
411 super("Should be launched from a single worker thread");
412 }
413 }
414
415 public abstract static class SipRunnable implements Runnable {
416 protected abstract void doRun() throws SameThreadException;
417
418 public void run() {
419 try {
420 doRun();
421 }catch(SameThreadException e) {
422 Log.e(TAG, "Not done from same thread");
423 }
424 }
425 }
426
Alexandre Savard31d27c62012-10-04 16:05:08 -0400427 public abstract static class SipRunnableWithReturn implements Runnable {
428 Object obj = null;
429 boolean done = false;
430
431 protected abstract Object doRun() throws SameThreadException;
432
433 public Object getVal() {
434 return obj;
435 }
436
437 public boolean isDone() {
438 return done;
439 }
440
441 public void run() {
442 try {
443 obj = doRun();
444 done = true;
445 }catch(SameThreadException e) {
446 Log.e(TAG, "Not done from same thread");
447 }
448 }
449 }
450
Emeric Vigier6119d782012-09-21 18:04:14 -0400451 class StartRunnable extends SipRunnable {
452 @Override
453 protected void doRun() throws SameThreadException {
454 startPjSipStack();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400455 }
456 }
457
458 private class SipServiceThread extends Thread {
459
460 public SipServiceThread() {
461 super("sipServiceThread");
462 }
463
464 @Override
465 public void run() {
Emeric Vigier12d61d82012-09-19 15:08:18 -0400466 Log.i(TAG, "SipService thread running...");
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400467 SipService sipService = SipService.this;
468 while(sipService.runFlag) {
469 try {
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400470 Thread.sleep(DELAY);
471 } catch (InterruptedException e) {
472 sipService.runFlag = false;
473 Log.w(TAG, "service thread interrupted!");
474 }
475 }
476 }
477 }
478}