blob: 04e191bd6c4b74327cf1b42b0a398fe483fade66 [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;
Alexandre Savard3bbb4792012-10-05 11:30:01 -040042import com.savoirfairelinux.sflphone.utils.AccountDetailsHandler;
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;
Emeric Vigier0007dee2012-09-24 11:35:58 -040060 private ManagerImpl managerImpl;
Emeric Vigier6119d782012-09-21 18:04:14 -040061 private boolean isPjSipStackStarted = false;
62
63 /* Implement public interface for the service */
64 private final ISipService.Stub mBinder = new ISipService.Stub() {
65
66 @Override
67 public void placeCall(final String accountID, final String callID, final String to) {
68 getExecutor().execute(new SipRunnable() {
69 @Override
70 protected void doRun() throws SameThreadException {
71 Log.i(TAG, "SipService.placeCall() thread running...");
72 callManagerJNI.placeCall(accountID, callID, to);
73 }
74 });
75 }
76
77 @Override
78 public void refuse(final String callID) {
79 getExecutor().execute(new SipRunnable() {
80 @Override
81 protected void doRun() throws SameThreadException {
82 Log.i(TAG, "SipService.refuse() thread running...");
83 callManagerJNI.refuse(callID);
84 }
85 });
86 }
87
88 @Override
89 public void accept(final String callID) {
90 getExecutor().execute(new SipRunnable() {
91 @Override
92 protected void doRun() throws SameThreadException {
93 Log.i(TAG, "SipService.placeCall() thread running...");
94 callManagerJNI.accept(callID);
95 }
96 });
97 }
98
99 @Override
100 public void hangUp(final String callID) {
101 getExecutor().execute(new SipRunnable() {
102 @Override
103 protected void doRun() throws SameThreadException {
104 Log.i(TAG, "SipService.hangUp() thread running...");
105 callManagerJNI.hangUp(callID);
106 }
107 });
108 }
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400109
110 @Override
111 public void setAudioPlugin(final String audioPlugin) {
112 getExecutor().execute(new SipRunnable() {
113 @Override
114 protected void doRun() throws SameThreadException {
115 Log.i(TAG, "SipService.setAudioPlugin() thread running...");
116 configurationManagerJNI.setAudioPlugin(audioPlugin);
117 }
Alexandre Savard31d27c62012-10-04 16:05:08 -0400118 });
119 }
120
121 @Override
122 public String getCurrentAudioOutputPlugin() {
123 class CurrentAudioPlugin extends SipRunnableWithReturn {
124 @Override
125 protected String doRun() throws SameThreadException {
126 Log.i(TAG, "SipService.getCurrentAudioOutputPlugin() thread running...");
127 return configurationManagerJNI.getCurrentAudioOutputPlugin();
128 }
129 };
130
131 CurrentAudioPlugin runInstance = new CurrentAudioPlugin();
132 getExecutor().execute(runInstance);
133 while(!runInstance.isDone()) {}
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400134 return (String) runInstance.getVal();
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400135 }
Alexandre Savard713a34d2012-09-26 15:50:41 -0400136
137 @Override
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400138 public ArrayList<String> getAccountList() {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400139 class AccountList extends SipRunnableWithReturn {
140 @Override
141 protected StringVect doRun() throws SameThreadException {
142 Log.i(TAG, "SipService.getAccountList() thread running...");
143 return configurationManagerJNI.getAccountList();
144 }
145 };
146 AccountList runInstance = new AccountList();
147 getExecutor().execute(runInstance);
148 while(!runInstance.isDone()) {}
149 StringVect swigvect = (StringVect) runInstance.getVal();
150
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400151 ArrayList<String> nativelist = new ArrayList<String>();
Alexandre Savard52a72522012-09-27 16:40:13 -0400152
153 for(int i = 0; i < swigvect.size(); i++)
154 nativelist.add(swigvect.get(i));
155
Alexandre Savard6b85e7e2012-09-27 15:43:14 -0400156 return nativelist;
157 }
158
159 @Override
Alexandre Savard713a34d2012-09-26 15:50:41 -0400160 public HashMap<String,String> getAccountDetails(final String accountID) {
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400161 class AccountDetails extends SipRunnableWithReturn {
162 private String id;
163 AccountDetails(String accountId) { id = accountId; }
164 @Override
165 protected StringMap doRun() throws SameThreadException {
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400166 Log.i(TAG, "SipService.getAccountDetails() thread running...");
Alexandre Savard7a902bc2012-10-04 16:32:35 -0400167 return configurationManagerJNI.getAccountDetails(id);
168 }
169 };
170
171 AccountDetails runInstance = new AccountDetails(accountID);
172 getExecutor().execute(runInstance);
173 while(!runInstance.isDone()) {}
174 StringMap swigmap = (StringMap) runInstance.getVal();
Alexandre Savard713a34d2012-09-26 15:50:41 -0400175
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400176 HashMap<String, String> nativemap = AccountDetailsHandler.convertSwigToNative(swigmap);
Alexandre Savard713a34d2012-09-26 15:50:41 -0400177
178 return nativemap;
179 }
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400180
181 @Override
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400182 public void setAccountDetails(final String accountId, Map map) {
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400183 HashMap<String,String> nativemap = (HashMap<String,String>) map;
184
Alexandre Savard3bbb4792012-10-05 11:30:01 -0400185 final StringMap swigmap = AccountDetailsHandler.convertFromNativeToSwig(nativemap);
Alexandre Savard718d49f2012-10-02 15:17:13 -0400186
Alexandre Savard7a2b2202012-10-04 17:07:33 -0400187 getExecutor().execute(new SipRunnable() {
188 @Override
189 protected void doRun() throws SameThreadException {
190 Log.i(TAG, "SipService.setAccountDetails() thread running...");
191 configurationManagerJNI.setAccountDetails(accountId, swigmap);
192 }
193 });
194
Alexandre Savard8b7d4332012-09-30 20:02:11 -0400195 }
196
Emeric Vigier6119d782012-09-21 18:04:14 -0400197 };
198
199 /**
200 * Class used for the client Binder. Because we know this service always
201 * runs in the same process as its clients, we don't need to deal with IPC.
202 */
203 public class LocalBinder extends Binder {
204 public SipService getService() {
205 // Return this instance of LocalService so clients can call public methods
206 return SipService.this;
207 }
208 }
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400209
210 /* called once by startService() */
211 @Override
212 public void onCreate() {
213 Log.i(TAG, "onCreated");
214 super.onCreate();
Emeric Vigier6119d782012-09-21 18:04:14 -0400215 sflphoneApp = (SFLphoneApplication) getApplication();
216 sipServiceThread = new SipServiceThread();
217 getExecutor().execute(new StartRunnable());
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400218 }
219
220 /* called for each startService() */
221 @Override
222 public int onStartCommand(Intent intent, int flags, int startId) {
223 Log.i(TAG, "onStarted");
224 super.onStartCommand(intent, flags, startId);
Emeric Vigier6119d782012-09-21 18:04:14 -0400225
226 runFlag = true;
227 sipServiceThread.start();
228 sflphoneApp.setServiceRunning(true);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400229 Toast.makeText(this, "Sflphone Service started", Toast.LENGTH_SHORT).show();
Emeric Vigier6119d782012-09-21 18:04:14 -0400230
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400231 Log.i(TAG, "onStarted");
232 return START_STICKY; /* started and stopped explicitly */
233 }
234
235 @Override
236 public void onDestroy() {
237 /* called once by stopService() */
238 super.onDestroy();
Emeric Vigier6119d782012-09-21 18:04:14 -0400239 runFlag = false;
240 sipServiceThread.interrupt();
241 sipServiceThread = null;
242 sflphoneApp.setServiceRunning(false);
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400243 Toast.makeText(this, "Sflphone Service stopped", Toast.LENGTH_SHORT).show();
244
245 Log.i(TAG, "onDestroyed");
246 }
247
248 @Override
249 public IBinder onBind(Intent arg0) {
250 Log.i(TAG, "onBound");
251 return mBinder;
252 }
253
Emeric Vigier6119d782012-09-21 18:04:14 -0400254 private static Looper createLooper() {
255 if(executorThread == null) {
256 Log.d(TAG, "Creating new handler thread");
257 // ADT gives a fake warning due to bad parse rule.
258 executorThread = new HandlerThread("SipService.Executor");
259 executorThread.start();
260 }
261 return executorThread.getLooper();
262 }
263
264 public SipServiceExecutor getExecutor() {
265 // create mExecutor lazily
266 if (mExecutor == null) {
267 mExecutor = new SipServiceExecutor(this);
268 }
269 return mExecutor;
270 }
271
272 // Executes immediate tasks in a single executorThread.
273 public static class SipServiceExecutor extends Handler {
274 WeakReference<SipService> handlerService;
275
276 SipServiceExecutor(SipService s) {
277 super(createLooper());
278 handlerService = new WeakReference<SipService>(s);
279 }
280
281 public void execute(Runnable task) {
282 // TODO: add wakelock
283 Message.obtain(this, 0/* don't care */, task).sendToTarget();
284 }
285
286 @Override
287 public void handleMessage(Message msg) {
288 if (msg.obj instanceof Runnable) {
289 executeInternal((Runnable) msg.obj);
290 } else {
291 Log.w(TAG, "can't handle msg: " + msg);
292 }
293 }
294
295 private void executeInternal(Runnable task) {
296 try {
297 task.run();
298 } catch (Throwable t) {
299 Log.e(TAG, "run task: " + task, t);
300 }
301 }
302 }
303
304 private void startPjSipStack() throws SameThreadException {
305 if (isPjSipStackStarted)
306 return;
307
308 try {
309 System.loadLibrary("gnustl_shared");
310 System.loadLibrary("expat");
311 System.loadLibrary("yaml");
312 System.loadLibrary("ccgnu2");
313 System.loadLibrary("crypto");
314 System.loadLibrary("ssl");
315 System.loadLibrary("ccrtp1");
316 System.loadLibrary("dbus");
317 System.loadLibrary("dbus-c++-1");
318 System.loadLibrary("samplerate");
319 System.loadLibrary("codec_ulaw");
320 System.loadLibrary("codec_alaw");
321 System.loadLibrary("speexresampler");
322 System.loadLibrary("sflphone");
323 isPjSipStackStarted = true;
324 } catch (UnsatisfiedLinkError e) {
325 Log.e(TAG, "Problem with the current Pj stack...", e);
326 isPjSipStackStarted = false;
327 return;
328 } catch (Exception e) {
329 Log.e(TAG, "Problem with the current Pj stack...", e);
330 }
331
Emeric Vigier0007dee2012-09-24 11:35:58 -0400332 /* get unique instance of managerImpl */
333 managerImpl = SFLPhoneservice.instance();
Alexandre Savard718d49f2012-10-02 15:17:13 -0400334
Emeric Vigier6119d782012-09-21 18:04:14 -0400335 /* set static AppPath before calling manager.init */
Emeric Vigier0007dee2012-09-24 11:35:58 -0400336 managerImpl.setPath(sflphoneApp.getAppPath());
Emeric Vigier6119d782012-09-21 18:04:14 -0400337
Alexandre Savard718d49f2012-10-02 15:17:13 -0400338 callManagerJNI = new CallManagerJNI();
Emeric Vigier0007dee2012-09-24 11:35:58 -0400339 callManagerCallBack = new CallManagerCallBack();
Alexandre Savard718d49f2012-10-02 15:17:13 -0400340
Emeric Vigier0007dee2012-09-24 11:35:58 -0400341 SFLPhoneservice.setCallbackObject(callManagerCallBack);
Emeric Vigier0007dee2012-09-24 11:35:58 -0400342
Alexandre Savardc1b08fe2012-09-25 16:24:47 -0400343 configurationManagerJNI = new ConfigurationManagerJNI();
344
Emeric Vigier0007dee2012-09-24 11:35:58 -0400345 managerImpl.init("");
Emeric Vigier6119d782012-09-21 18:04:14 -0400346 return;
347 }
348
349 // Enforce same thread contract to ensure we do not call from somewhere else
350 public class SameThreadException extends Exception {
351 private static final long serialVersionUID = -905639124232613768L;
352
353 public SameThreadException() {
354 super("Should be launched from a single worker thread");
355 }
356 }
357
358 public abstract static class SipRunnable implements Runnable {
359 protected abstract void doRun() throws SameThreadException;
360
361 public void run() {
362 try {
363 doRun();
364 }catch(SameThreadException e) {
365 Log.e(TAG, "Not done from same thread");
366 }
367 }
368 }
369
Alexandre Savard31d27c62012-10-04 16:05:08 -0400370 public abstract static class SipRunnableWithReturn implements Runnable {
371 Object obj = null;
372 boolean done = false;
373
374 protected abstract Object doRun() throws SameThreadException;
375
376 public Object getVal() {
377 return obj;
378 }
379
380 public boolean isDone() {
381 return done;
382 }
383
384 public void run() {
385 try {
386 obj = doRun();
387 done = true;
388 }catch(SameThreadException e) {
389 Log.e(TAG, "Not done from same thread");
390 }
391 }
392 }
393
Emeric Vigier6119d782012-09-21 18:04:14 -0400394 class StartRunnable extends SipRunnable {
395 @Override
396 protected void doRun() throws SameThreadException {
397 startPjSipStack();
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400398 }
399 }
400
401 private class SipServiceThread extends Thread {
402
403 public SipServiceThread() {
404 super("sipServiceThread");
405 }
406
407 @Override
408 public void run() {
Emeric Vigier12d61d82012-09-19 15:08:18 -0400409 Log.i(TAG, "SipService thread running...");
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400410 SipService sipService = SipService.this;
411 while(sipService.runFlag) {
412 try {
Emeric Vigiereaf2c492012-09-19 14:38:20 -0400413 Thread.sleep(DELAY);
414 } catch (InterruptedException e) {
415 sipService.runFlag = false;
416 Log.w(TAG, "service thread interrupted!");
417 }
418 }
419 }
420 }
421}