Newer
Older
/**
* Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
*
* Author: Regis Montoya <r3gis.3R@gmail.com>
* Author: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
* Alexandre Lision <alexandre.lision@savoirfairelinux.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* If you own a pjsip commercial license you can also redistribute it
* and/or modify it under the terms of the GNU Lesser General Public License
* as an android library.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.os.Handler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Service;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import cx.ring.history.HistoryManager;
import cx.ring.model.Codec;
import cx.ring.model.Conference;
import cx.ring.model.SecureSipCall;
import cx.ring.model.SipMessage;
import cx.ring.utils.MediaManager;
import cx.ring.utils.SipNotifications;
import cx.ring.utils.SwigNativeConverter;
import cx.ring.model.SipCall;
public class SipService extends Service {
static final String TAG = "SipService";
private SipServiceExecutor mExecutor;
private static HandlerThread executorThread;
private Handler handler = new Handler();
private static int POLLING_TIMEOUT = 500;
private Runnable pollEvents = new Runnable() {
@Override
public void run() {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Ringservice.pollEvents();
}
});
handler.postDelayed(this, POLLING_TIMEOUT);
private boolean isPjSipStackStarted = false;
protected SipNotifications mNotificationManager;
protected HistoryManager mHistoryManager;
protected MediaManager mMediaManager;
private HashMap<String, Conference> mConferences = new HashMap<>();
private ConfigurationManagerCallback configurationCallback;
private CallManagerCallBack callManagerCallBack;
public HashMap<String, Conference> getConferences() {
return mConferences;
public void addCallToConference(String confId, String callId) {
if(mConferences.get(callId) != null){
// We add a simple call to a conference
Log.i(TAG, "// We add a simple call to a conference");
mConferences.get(confId).addParticipant(mConferences.get(callId).getParticipants().get(0));
mConferences.remove(callId);
} else {
Log.i(TAG, "addCallToConference");
for (Entry<String, Conference> stringConferenceEntry : mConferences.entrySet()) {
Conference tmp = stringConferenceEntry.getValue();
for (SipCall c : tmp.getParticipants()) {
if (c.getCallId().contentEquals(callId)) {
mConferences.get(confId).addParticipant(c);
mConferences.get(tmp.getId()).removeParticipant(c);
}
}
}
}
}
public void detachCallFromConference(String confId, SipCall call) {
Log.i(TAG, "detachCallFromConference");
Conference separate = new Conference(call);
mConferences.put(separate.getId(), separate);
mConferences.get(confId).removeParticipant(call);
@Override
public boolean onUnbind(Intent i) {
super.onUnbind(i);
Log.i(TAG, "onUnbind(intent)");
public void onRebind(Intent i) {
}
/* called once by startService() */
@Override
public void onCreate() {
Log.i(TAG, "onCreated");
super.onCreate();
getExecutor().execute(new StartRunnable());
mNotificationManager = new SipNotifications(this);
mMediaManager = new MediaManager(this);
mHistoryManager = new HistoryManager(this);
mNotificationManager.onServiceCreate();
mMediaManager.startService();
/* called for each startService() */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStarted " + (intent == null ? "null" : intent.getAction()) + " " + flags);
super.onStartCommand(intent, flags, startId);
return START_STICKY; /* started and stopped explicitly */
}
@Override
public void onDestroy() {
/* called once by stopService() */
mNotificationManager.onServiceDestroy();
mMediaManager.stopService();
getExecutor().execute(new FinalizeRunnable());
super.onDestroy();
}
@Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "onBound");
return mBinder;
}
private static Looper createLooper() {
if (executorThread == null) {
Log.d(TAG, "Creating new handler thread");
// ADT gives a fake warning due to bad parse rule.
executorThread = new HandlerThread("SipService.Executor");
executorThread.start();
}
return executorThread.getLooper();
}
public SipServiceExecutor getExecutor() {
// create mExecutor lazily
if (mExecutor == null) {
mExecutor = new SipServiceExecutor();
}
return mExecutor;
}
public SipCall getCallById(String callID) {
if (getConferences().get(callID) != null) {
return getConferences().get(callID).getCallById(callID);
} else {
// Check if call is in a conference
for (Entry<String, Conference> stringConferenceEntry : getConferences().entrySet()) {
Conference tmp = stringConferenceEntry.getValue();
SipCall c = tmp.getCallById(callID);
}
}
return null;
}
// Executes immediate tasks in a single executorThread.
public static class SipServiceExecutor extends Handler {
super(createLooper());
}
public void execute(Runnable task) {
// TODO: add wakelock
Message.obtain(SipServiceExecutor.this, 0/* don't care */, task).sendToTarget();
}
@Override
public void handleMessage(Message msg) {
if (msg.obj instanceof Runnable) {
executeInternal((Runnable) msg.obj);
} else {
Log.w(TAG, "can't handle msg: " + msg);
}
}
private void executeInternal(Runnable task) {
try {
task.run();
} catch (Throwable t) {
Log.e(TAG, "run task: " + task, t);
}
}
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
public final boolean executeSynced(final Runnable r) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (Looper.myLooper() == getLooper()) {
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, 0);
}
public final <T> T executeAndReturn(final SipRunnableWithReturn<T> r) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (Looper.myLooper() == getLooper()) {
r.run();
return r.getVal();
}
BlockingRunnable br = new BlockingRunnable(r);
if (!br.postAndWait(this, 0))
throw new RuntimeException("Can't execute runnable");
return r.getVal();
}
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
}
private void stopDaemon() {
handler.removeCallbacks(pollEvents);
if (isPjSipStackStarted) {
Ringservice.fini();
isPjSipStackStarted = false;
Log.i(TAG, "PjSIPStack stopped");
}
private void startPjSipStack() throws SameThreadException {
if (isPjSipStackStarted)
return;
try {
isPjSipStackStarted = true;
} catch (UnsatisfiedLinkError e) {
Log.e(TAG, "Problem with the current Pj stack...", e);
isPjSipStackStarted = false;
return;
} catch (Exception e) {
Log.e(TAG, "Problem with the current Pj stack...", e);
isPjSipStackStarted = false;
}
configurationCallback = new ConfigurationManagerCallback(this);
callManagerCallBack = new CallManagerCallBack(this);
Ringservice.init(configurationCallback, callManagerCallBack);
handler.postDelayed(pollEvents, POLLING_TIMEOUT);
Log.i(TAG, "PjSIPStack started");
}
// Enforce same thread contract to ensure we do not call from somewhere else
public class SameThreadException extends Exception {
private static final long serialVersionUID = -905639124232613768L;
public SameThreadException() {
super("Should be launched from a single worker thread");
}
}
public abstract static class SipRunnable implements Runnable {
protected abstract void doRun() throws SameThreadException, RemoteException;
public void run() {
try {
doRun();
} catch (SameThreadException e) {
Log.e(TAG, "Not done from same thread");
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
}
}
public abstract class SipRunnableWithReturn<T> implements Runnable {
private T obj = null;
//boolean done = false;
protected abstract T doRun() throws SameThreadException, RemoteException;
return obj;
}
public void run() {
try {
if (isPjSipStackStarted)
obj = doRun();
else
Log.e(TAG, "Can't perform operation: daemon not started.");
//done = true;
} catch (SameThreadException e) {
Log.e(TAG, "Not done from same thread");
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
}
}
class StartRunnable extends SipRunnable {
@Override
protected void doRun() throws SameThreadException {
startPjSipStack();
}
}
class FinalizeRunnable extends SipRunnable {
@Override
protected void doRun() throws SameThreadException {
stopDaemon();
}
}
/* ************************************
* Implement public interface for the service
* *********************************
*/
private final ISipService.Stub mBinder = new ISipService.Stub() {
@Override
public String placeCall(final SipCall call) {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.placeCall() thread running...");
Conference toAdd;
if(call.getAccount().useSecureLayer()){
SecureSipCall secureCall = new SecureSipCall(call);
toAdd = new Conference(secureCall);
} else {
toAdd = new Conference(call);
}
mConferences.put(toAdd.getId(), toAdd);
mMediaManager.obtainAudioFocus(false);
return Ringservice.placeCall(call.getAccount().getAccountID(), call.getmContact().getPhones().get(0).getNumber());
}
});
}
@Override
public void refuse(final String callID) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.refuse() thread running...");
}
});
}
@Override
public void accept(final String callID) {
mMediaManager.stopRing();
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.accept() thread running...");
mMediaManager.RouteToInternalSpeaker();
}
});
}
@Override
public void hangUp(final String callID) {
mMediaManager.stopRing();
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.hangUp() thread running...");
removeCall(callID);
if(mConferences.size() == 0) {
Log.i(TAG, "No more calls!");
mMediaManager.abandonAudioFocus();
}
});
}
@Override
public void hold(final String callID) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.hold() thread running...");
}
});
}
@Override
public void unhold(final String callID) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.unhold() thread running...");
}
});
}
public Map<String, String> getCallDetails(final String callID) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
protected Map<String, String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getCallDetails() thread running...");
return Ringservice.getCallDetails(callID).toNative();
@Override
public void setAudioPlugin(final String audioPlugin) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.setAudioPlugin() thread running...");
Alexandre Savard
committed
});
}
@Override
public String getCurrentAudioOutputPlugin() {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
Alexandre Savard
committed
@Override
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.getCurrentAudioOutputPlugin() thread running...");
return Ringservice.getCurrentAudioOutputPlugin();
Alexandre Savard
committed
}
@Override
public List<String> getAccountList() {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<List<String>>() {
protected List<String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getAccountList() thread running...");
return new ArrayList<>(Ringservice.getAccountList());
public void setAccountOrder(final String order) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.setAccountsOrder() thread running...");
public Map<String, String> getAccountDetails(final String accountID) {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
protected Map<String, String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getAccountDetails() thread running...");
return Ringservice.getAccountDetails(accountID).toNative();
@SuppressWarnings("unchecked")
// Hashmap runtime cast
public void setAccountDetails(final String accountId, final Map map) {
Log.i(TAG, "SipService.setAccountDetails() " + map.get("Account.hostname"));
final StringMap swigmap = StringMap.toSwig(map);
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Ringservice.setAccountDetails(accountId, swigmap);
Log.i(TAG, "SipService.setAccountDetails() thread running... " + swigmap.get("Account.hostname"));
}
public Map<String, String> getVolatileAccountDetails(final String accountId) {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
@Override
protected Map<String, String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getVolatileAccountDetails() thread running...");
return Ringservice.getVolatileAccountDetails(accountId).toNative();
}
});
}
@Override
public Map<String, String> getAccountTemplate(final String accountType) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
protected Map<String, String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getAccountTemplate() thread running...");
return Ringservice.getAccountTemplate(accountType).toNative();
@SuppressWarnings("unchecked")
// Hashmap runtime cast
@Override
public String addAccount(final Map map) {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
@Override
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.addAccount() thread running...");
return Ringservice.addAccount(StringMap.toSwig(map));
@Override
public void removeAccount(final String accountId) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException {
Log.i(TAG, "SipService.setAccountDetails() thread running...");
}
});
}
/*************************
* Transfer related API
*************************/
@Override
public void transfer(final String callID, final String to) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.transfer() thread running...");
Bundle bundle = new Bundle();
bundle.putString("CallID", callID);
bundle.putString("State", "HUNGUP");
Intent intent = new Intent(CallManagerCallBack.CALL_STATE_CHANGED);
intent.putExtra("com.savoirfairelinux.sflphone.service.newstate", bundle);
} else
Log.i(TAG, "NOT OK");
}
});
@Override
public void attendedTransfer(final String transferID, final String targetID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.attendedTransfer() thread running...");
if (Ringservice.attendedTransfer(transferID, targetID)) {
Log.i(TAG, "OK");
} else
Log.i(TAG, "NOT OK");
}
});
/*************************
* Conference related API
*************************/
@Override
public void removeConference(final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.createConference() thread running...");
}
});
}
@Override
public void joinParticipant(final String sel_callID, final String drag_callID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.joinParticipant() thread running...");
Ringservice.joinParticipant(sel_callID, drag_callID);
}
});
Log.i(TAG, "After joining participants");
@Override
public void addParticipant(final SipCall call, final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.addParticipant() thread running...");
Ringservice.addParticipant(call.getCallId(), confID);
mConferences.get(confID).getParticipants().add(call);
}
});
}
@Override
public void addMainParticipant(final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.addMainParticipant() thread running...");
}
});
}
@Override
public void detachParticipant(final String callID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.detachParticipant() thread running...");
Iterator<Entry<String, Conference>> it = mConferences.entrySet().iterator();
Log.i(TAG, "mConferences size " + mConferences.size());
while (it.hasNext()) {
Conference tmp = it.next().getValue();
Log.i(TAG, "conf has " + tmp.getParticipants().size() + " participants");
if (tmp.contains(callID)) {
Conference toDetach = new Conference(tmp.getCallById(callID));
mConferences.put(toDetach.getId(), toDetach);
Log.i(TAG, "Call found and put in current_calls");
}
}
}
});
@Override
public void joinConference(final String sel_confID, final String drag_confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.joinConference() thread running...");
Ringservice.joinConference(sel_confID, drag_confID);
}
});
Alexandre Savard
committed
@Override
public void hangUpConference(final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.joinConference() thread running...");
}
});
Alexandre Savard
committed
}
@Override
public void holdConference(final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.holdConference() thread running...");
}
});
Alexandre Savard
committed
}
@Override
public void unholdConference(final String confID) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.unholdConference() thread running...");
}
});
Alexandre Savard
committed
}
@Override
public boolean isConferenceParticipant(final String callID) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Boolean>() {
@Override
protected Boolean doRun() throws SameThreadException {
Log.i(TAG, "SipService.isRecording() thread running...");
return Ringservice.isConferenceParticipant(callID);
Alexandre Savard
committed
public HashMap<String, Conference> getConferenceList() throws RemoteException {
// class ConfList extends SipRunnableWithReturn {
// @Override
// protected StringVect doRun() throws SameThreadException {
// Log.i(TAG, "SipService.getConferenceList() thread running...");
// return callManagerJNI.getConferenceList();
// }
// }
// ;
// ConfList runInstance = new ConfList();
// getExecutor().execute(runInstance);
// while (!runInstance.isDone()) {
// // Log.w(TAG, "Waiting for getConferenceList");
// }
// StringVect swigvect = (StringVect) runInstance.getVal();
//
// ArrayList<String> nativelist = new ArrayList<String>();
//
// for (int i = 0; i < swigvect.size(); i++)
// nativelist.add(swigvect.get(i));
//
// return nativelist;
return mConferences;
@Override
public List<String> getParticipantList(final String confID) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<List<String>>() {
protected List<String> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getParticipantList() thread running...");
return new ArrayList<>(Ringservice.getParticipantList(confID));
@Override
public String getConferenceId(String callID) throws RemoteException {
Log.e(TAG, "getConferenceList not implemented");
return null;
public String getConferenceDetails(final String callID) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.getConferenceDetails() thread running...");
return Ringservice.getConferenceDetails(callID).get("CONF_STATE");
@Override
public String getRecordPath() throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
@Override
protected String doRun() throws SameThreadException {
Log.i(TAG, "SipService.getRecordPath() thread running...");
public boolean toggleRecordingCall(final String id) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<Boolean>() {
protected Boolean doRun() throws SameThreadException {
Log.i(TAG, "SipService.toggleRecordingCall() thread running...");
boolean result = Ringservice.toggleRecording(id);
if (getConferences().containsKey(id)) {
getConferences().get(id).setRecording(result);
for (Conference c : getConferences().values()) {
if (c.getCallById(id) != null)
c.getCallById(id).setRecording(result);
@Override
public boolean startRecordedFilePlayback(final String filepath) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.setRecordingCall() thread running...");
Ringservice.startRecordedFilePlayback(filepath);
}
});
return false;
}
@Override
public void stopRecordedFilePlayback(final String filepath) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.stopRecordedFilePlayback() thread running...");
@Override
public void setRecordPath(final String path) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.setRecordPath() " + path + " thread running...");
public void sendTextMessage(final String callID, final SipMessage message) throws RemoteException {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.sendTextMessage() thread running...");
StringMap messages = new StringMap();
messages.set("text/plain", message.comment);
Ringservice.sendTextMessage(callID, messages, "", false);
if (getConferences().get(callID) != null)
getConferences().get(callID).addSipMessage(message);
@Override
public void sendAccountTextMessage(final String accountid, final String to, final String msg) {
getExecutor().execute(new SipRunnable() {
@Override
protected void doRun() throws SameThreadException, RemoteException {
Log.i(TAG, "SipService.sendAccountTextMessage() thread running... " + accountid + " " + to + " " + msg);
Ringservice.sendAccountTextMessage(accountid, to, msg);
}
});
}
public List<Codec> getAudioCodecList(final String accountID) throws RemoteException {
return getExecutor().executeAndReturn(new SipRunnableWithReturn<ArrayList<Codec>>() {
protected ArrayList<Codec> doRun() throws SameThreadException {
Log.i(TAG, "SipService.getAudioCodecList() thread running...");
ArrayList<Codec> results = new ArrayList<>();
UintVect active_payloads = Ringservice.getActiveCodecList(accountID);
for (int i = 0; i < active_payloads.size(); ++i) {
Log.i(TAG, "SipService.getCodecDetails(" + accountID +", "+ active_payloads.get(i) +")");
results.add(new Codec(active_payloads.get(i), Ringservice.getCodecDetails(accountID, active_payloads.get(i)), true));
for (int i = 0; i < payloads.size(); ++i) {
boolean isActive = false;
for (Codec co : results) {
if (co.getPayload() == payloads.get(i))
StringMap details = Ringservice.getCodecDetails(accountID, payloads.get(i));
if (details.size() > 1)
results.add(new Codec(payloads.get(i), Ringservice.getCodecDetails(accountID, payloads.get(i)), false));
else
Log.i(TAG, "Error loading codec " + i);
}
return results;
@Override
public Map getRingtoneList() throws RemoteException {
class RingtoneList extends SipRunnableWithReturn {
@Override
protected StringMap doRun() throws SameThreadException {
Log.i(TAG, "SipService.getRingtoneList() thread running...");