Skip to content
Snippets Groups Projects
SipService.java 48.8 KiB
Newer Older
/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
Alexandre Lision's avatar
Alexandre Lision committed
 * Copyright (C) 2004-2014 Savoir-Faire Linux Inc.
 *
 *  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/>.
 */
package cx.ring.service;
import android.os.Handler;

import java.util.ArrayList;
import java.util.HashMap;
alision's avatar
alision committed
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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<>();
Alexandre Lision's avatar
Alexandre Lision committed
    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) {
        super.onRebind(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 */
Alexandre Lision's avatar
Alexandre Lision committed
        Log.i(TAG, "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();
    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);
                if (c != null)
                    return c;
    // Executes immediate tasks in a single executorThread.
    public static class SipServiceExecutor extends Handler {

        SipServiceExecutor() {
            super(createLooper());
        }

        public void execute(Runnable task) {
            // TODO: add wakelock
            Message.obtain(SipServiceExecutor.this, 0/* don't care */, task).sendToTarget();
            //Log.w(TAG, "SenT!");
        }

        @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);
            }
        }

        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 {
Adrien Béraud's avatar
Adrien Béraud committed
            System.loadLibrary("ringjni");
        } 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;
Alexandre Lision's avatar
Alexandre Lision committed
        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;
                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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.refuse(callID);
                }
            });
        }

        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.accept(callID);
                    mMediaManager.RouteToInternalSpeaker();
                }
            });
        }

        @Override
        public void hangUp(final String callID) {
            mMediaManager.stopRing();
            Log.e(TAG, "HANGING UP");
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.hangUp() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.hangUp(callID);
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.hold(callID);
                }
            });
        }

        @Override
        public void unhold(final String callID) {
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.unhold() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.unhold(callID);
        @Override
        public Map<String, String> getCallDetails(final String callID) throws RemoteException {
            return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
                @Override
Adrien Béraud's avatar
Adrien Béraud committed
                protected Map<String, String> doRun() throws SameThreadException {
Alexandre Lision's avatar
Alexandre Lision committed
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.setAudioPlugin(audioPlugin);
            });
        }

        @Override
        public String getCurrentAudioOutputPlugin() {
            return getExecutor().executeAndReturn(new SipRunnableWithReturn<String>() {
                @Override
                protected String doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.getCurrentAudioOutputPlugin() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    return Ringservice.getCurrentAudioOutputPlugin();
        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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.setAccountsOrder(order);
Adrien Béraud's avatar
Adrien Béraud committed
        public Map<String, String> getAccountDetails(final String accountID) {
            return getExecutor().executeAndReturn(new SipRunnableWithReturn<Map<String, String>>() {
Adrien Béraud's avatar
Adrien Béraud committed
                protected Map<String, String> doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.getAccountDetails() thread running...");
                    return Ringservice.getAccountDetails(accountID).toNative();
        @SuppressWarnings("unchecked")
        // Hashmap runtime cast
alision's avatar
alision committed
        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 {
Adrien Béraud's avatar
Adrien Béraud committed
                    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>>() {
Adrien Béraud's avatar
Adrien Béraud committed
                protected Map<String, String> doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.getAccountTemplate() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    return Ringservice.getAccountTemplate(accountType).toNative();
        @SuppressWarnings("unchecked")
        // Hashmap runtime cast
        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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.removeAccount(accountId);
        /*************************
         * 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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    if (Ringservice.transfer(callID, to)) {
                        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);
                        sendBroadcast(intent);
        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    if (Ringservice.attendedTransfer(transferID, targetID)) {
        /*************************
         * 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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.removeConference(confID);
        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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.joinParticipant(sel_callID, drag_callID);
                    // Generate a CONF_CREATED callback
            Log.i(TAG, "After joining participants");
        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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.addMainParticipant(confID);
        @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...");
                    Log.i(TAG, "Detaching " + callID);
                    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");
                        }
                    }
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.detachParticipant(callID);
        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.joinConference(sel_confID, drag_confID);
        @Override
        public void hangUpConference(final String confID) throws RemoteException {
            Log.e(TAG, "HANGING UP CONF");
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException, RemoteException {
                    Log.i(TAG, "SipService.joinConference() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.hangUpConference(confID);
        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.holdConference(confID);
        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.unholdConference(confID);
        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    return Ringservice.isConferenceParticipant(callID);
        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;
        public List<String> getParticipantList(final String confID) throws RemoteException {
            return getExecutor().executeAndReturn(new SipRunnableWithReturn<List<String>>() {
alision's avatar
alision committed
                @Override
                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>() {
                @Override
Adrien Béraud's avatar
Adrien Béraud committed
                protected String doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.getConferenceDetails() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    return Ringservice.getRecordPath();
        public boolean toggleRecordingCall(final String id) throws RemoteException {
            return getExecutor().executeAndReturn(new SipRunnableWithReturn<Boolean>() {
                @Override
                protected Boolean doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.toggleRecordingCall() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.stopRecordedFilePlayback(filepath);

        @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...");
Adrien Béraud's avatar
Adrien Béraud committed
                    Ringservice.setRecordPath(path);
        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<>();
Adrien Béraud's avatar
Adrien Béraud committed
                    UintVect active_payloads = Ringservice.getActiveCodecList(accountID);
                    for (int i = 0; i < active_payloads.size(); ++i) {
Adrien Béraud's avatar
Adrien Béraud committed
                        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));
Adrien Béraud's avatar
Adrien Béraud committed
                    UintVect payloads = Ringservice.getCodecList();

                    for (int i = 0; i < payloads.size(); ++i) {
                        boolean isActive = false;
                        for (Codec co : results) {
                            if (co.getPayload() == payloads.get(i))
                                isActive = true;
Adrien Béraud's avatar
Adrien Béraud committed
                            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);
                        }
        @Override
        public Map getRingtoneList() throws RemoteException {
            class RingtoneList extends SipRunnableWithReturn {

                @Override
                protected StringMap doRun() throws SameThreadException {
                    Log.i(TAG, "SipService.getRingtoneList() thread running...");
Adrien Béraud's avatar
Adrien Béraud committed
                    return Ringservice.getR();