org.simlar.SimlarService.java Source code

Java tutorial

Introduction

Here is the source code for org.simlar.SimlarService.java

Source

/**
 * Copyright (C) 2013 The Simlar Authors.
 *
 * This file is part of Simlar. (http://www.simlar.org)
 *
 * 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 2
 * of the License, or (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package org.simlar;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.simlar.PreferencesHelper.NotInitedException;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import android.provider.ContactsContract;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;

public class SimlarService extends Service implements LinphoneHandlerListener {
    static final String LOGTAG = SimlarService.class.getSimpleName();
    private static final int NOTIFICATION_ID = 1;

    LinphoneThread mLinphoneThread = null;
    Handler mHandler = new Handler();
    private final IBinder mBinder = new SimlarServiceBinder();
    Map<String, ContactData> mContacts = new HashMap<String, ContactData>();
    private SimlarStatus mSimlarStatus = SimlarStatus.OFFLINE;
    private SimlarCallState mSimlarCallState = new SimlarCallState();
    private WakeLock mWakeLock = null;
    private WifiLock mWifiLock = null;
    private boolean mGoingDown = false;
    private boolean mTerminatePrivateAlreadyCalled = false;
    private boolean mCreatingAccount = false;
    private Class<?> mNotificationActivity = null;
    private VibratorThread mVibratorThread = null;
    private RingtoneThread mRingtoneThread = null;
    private boolean mResumeMusicAfterCall = false;
    private NetworkChangeReceiver mNetworkChangeReceiver = new NetworkChangeReceiver();
    private PendingIntent mkeepAwakePendingIntent = null;
    private KeepAwakeReceiver mKeepAwakeReceiver = new KeepAwakeReceiver();

    public class SimlarServiceBinder extends Binder {
        SimlarService getService() {
            return SimlarService.this;
        }
    }

    class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            SimlarService.this.checkNetworkConnectivityAndRefreshRegisters();
        }
    }

    class KeepAwakeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            SimlarService.this.keepAwake();
        }
    }

    public class ContactData {
        public String name;
        public ContactStatus status;
        public String photoId;

        public ContactData(final String name, final ContactStatus status, final String photoId) {
            this.name = name;
            this.status = status;
            this.photoId = photoId;
        }

        public boolean isRegistered() {
            return status.isRegistered();
        }

        public boolean isOnline() {
            return status.isOnline();
        }
    }

    public class FullContactData extends ContactData {
        public String number;

        public FullContactData(final String number, final String name, final ContactStatus status,
                final String photoId) {
            super(name, status, photoId);
            this.number = number;
        }

        public FullContactData(final String number, final ContactData cd) {
            super(cd.name, cd.status, cd.photoId);
            this.number = number;
        }

        public String getNameOrNumber() {
            if (Util.isNullOrEmpty(name)) {
                return number;
            }

            return name;
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        Log.i(LOGTAG, "onBind");
        return mBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOGTAG, "onStartCommand intent='" + intent + "' startId='" + startId + "'");

        // We want this service to continue running until it is explicitly stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        Log.i(LOGTAG, "started on device: " + Build.DEVICE);

        FileHelper.init(this);
        mVibratorThread = new VibratorThread(this.getApplicationContext());
        mRingtoneThread = new RingtoneThread(this.getApplicationContext());

        mWakeLock = ((PowerManager) this.getSystemService(Context.POWER_SERVICE))
                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "SimlarWakeLock");
        mWifiLock = ((WifiManager) this.getSystemService(Context.WIFI_SERVICE))
                .createWifiLock(WifiManager.WIFI_MODE_FULL, "SimlarWifiLock");

        startForeground(NOTIFICATION_ID, createNotification(SimlarStatus.OFFLINE));

        mLinphoneThread = new LinphoneThread(this, this);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(mNetworkChangeReceiver, intentFilter);

        startKeepAwake();

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                initializeCredentials();
            }
        });
    }

    public void registerActivityToNotification(final Class<?> activity) {
        if (activity == null) {
            Log.e(LOGTAG, "registerActivityToNotification with empty activity");
            return;
        }

        Log.i(LOGTAG, "registerActivityToNotification: " + activity.getSimpleName());
        mNotificationActivity = activity;

        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.notify(NOTIFICATION_ID, createNotification(mSimlarStatus));
    }

    Notification createNotification(final SimlarStatus status) {
        String text = null;

        if (mNotificationActivity == null || (status != SimlarStatus.ONGOING_CALL && !mCreatingAccount)) {
            mNotificationActivity = MainActivity.class;
        }

        final PendingIntent activity = PendingIntent.getActivity(this, 0,
                new Intent(this, mNotificationActivity).addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED), 0);

        if (status == SimlarStatus.ONGOING_CALL) {
            text = String.format(getString(status.getNotificationTextId()), mSimlarCallState.getDisplayName());
        } else if (mCreatingAccount) {
            text = getString(R.string.notification_simlar_status_creating_account);
            if (!Util.isNullOrEmpty(PreferencesHelper.getMySimlarIdOrEmptyString())) {
                text += ": " + String.format(getString(status.getNotificationTextId()),
                        PreferencesHelper.getMySimlarIdOrEmptyString());
            }
        } else {
            text = String.format(getString(status.getNotificationTextId()),
                    PreferencesHelper.getMySimlarIdOrEmptyString());
        }

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
        notificationBuilder.setSmallIcon(status.getNotificationIcon());
        notificationBuilder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.app_logo));
        notificationBuilder.setContentTitle(getString(R.string.app_name));
        notificationBuilder.setContentText(text);
        notificationBuilder.setTicker(text);
        notificationBuilder.setOngoing(true);
        notificationBuilder.setContentIntent(activity);
        notificationBuilder.setWhen(System.currentTimeMillis());
        return notificationBuilder.build();
    }

    void initializeCredentials() {
        notifySimlarStatusChanged(SimlarStatus.OFFLINE);

        if (PreferencesHelper.readPrefencesFromFile(this)) {
            connect();
        } else {
            mCreatingAccount = true;
            notifySimlarStatusChanged(mSimlarStatus);
            startActivity(new Intent(this, VerifyNumberActivity.class)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
        }
    }

    public void connect() {
        notifySimlarStatusChanged(SimlarStatus.CONNECTING);

        try {
            mLinphoneThread.register(PreferencesHelper.getMySimlarId(), PreferencesHelper.getPassword());
        } catch (NotInitedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public synchronized void onDestroy() {
        Log.i(LOGTAG, "onDestroy");

        unregisterReceiver(mNetworkChangeReceiver);

        stopKeepAwake();

        // just in case
        releaseWakeLock();
        releaseWifiLock();

        // Tell the user we stopped.
        Toast.makeText(this, R.string.simlarservice_on_destroy, Toast.LENGTH_SHORT).show();

        Log.i(LOGTAG, "onDestroy ended");
    }

    private void acquireWakeLock() {
        if (!mWakeLock.isHeld()) {
            mWakeLock.acquire();
        }
    }

    private void acquireWifiLock() {
        if (!mWifiLock.isHeld()) {
            mWifiLock.acquire();
        }
    }

    void releaseWakeLock() {
        if (mWakeLock.isHeld()) {
            mWakeLock.release();
        }
    }

    private void releaseWifiLock() {
        if (mWifiLock.isHeld()) {
            mWifiLock.release();
        }
    }

    void checkNetworkConnectivityAndRefreshRegisters() {
        final NetworkInfo ni = ((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE))
                .getActiveNetworkInfo();

        if (ni == null) {
            Log.e(LOGTAG, "no NetworkInfo found");
            return;
        }

        Log.i(LOGTAG, "NetworkInfo " + ni.getTypeName() + " " + ni.getState());
        if (ni.isConnected()) {
            mLinphoneThread.refreshRegisters();
        }
    }

    private void startKeepAwake() {
        final Intent startIntent = new Intent("org.simlar.keepAwake");
        mkeepAwakePendingIntent = PendingIntent.getBroadcast(this, 0, startIntent, 0);

        ((AlarmManager) getSystemService(Context.ALARM_SERVICE)).setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + 600000, 600000, mkeepAwakePendingIntent);

        IntentFilter filter = new IntentFilter();
        filter.addAction("org.simlar.keepAwake");
        registerReceiver(mKeepAwakeReceiver, filter);
    }

    private void stopKeepAwake() {
        unregisterReceiver(mKeepAwakeReceiver);
        ((AlarmManager) getSystemService(Context.ALARM_SERVICE)).cancel(mkeepAwakePendingIntent);
    }

    void keepAwake() {
        // idea from linphones KeepAliveHandler

        checkNetworkConnectivityAndRefreshRegisters();

        // make sure iterate will have enough time before device eventually goes to sleep
        acquireWakeLock();
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (!getSimlarCallState().isNewCall()) {
                    releaseWakeLock();
                }
            }
        }, 4000);
    }

    @Override
    public void onRegistrationStateChanged(final RegistrationState state) {
        Log.i(LOGTAG, "handleRegistrationStateChanged: " + state.toString());

        SimlarStatus status = SimlarStatus.fromRegistrationState(state);

        if (mCreatingAccount) {
            if (status.isRegistrationAtSipServerFailed()) {
                Log.i(LOGTAG, "creating account: registration failed");
                //mLinphoneThread.unregister();
                SimlarServiceBroadcast.sendTestRegistrationFailed(this);
            }

            if (status.isConnectedToSipServer()) {
                Log.i(LOGTAG, "creating account: registration succes");
                mCreatingAccount = false;

                SimlarServiceBroadcast.sendTestRegistrationSuccess(this);
            }
        }

        if (state == RegistrationState.RegistrationOk) {
            loadContactsFromTelephonebook();
            status = SimlarStatus.LOADING_CONTACTS;
        }

        if (mGoingDown && !status.isConnectedToSipServer()) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    terminatePrivate();
                }
            });
        }

        notifySimlarStatusChanged(status);
    }

    void notifySimlarStatusChanged(final SimlarStatus status) {
        Log.i(LOGTAG, "notifySimlarStatusChanged: " + status);

        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        nm.notify(NOTIFICATION_ID, createNotification(status));

        mSimlarStatus = status;

        SimlarServiceBroadcast.sendSimlarStatusChanged(this);
    }

    @Override
    public void onCallStatsChanged(final float upload, final float download, final float quality,
            final String codec, final String iceState) {
        if (!mSimlarCallState.updateCallStats(upload, download, quality, codec, iceState)) {
            Log.d(LOGTAG, "IceStateChanged but SimlarCallState unchanged: " + mSimlarCallState);
            return;
        }

        if (mSimlarCallState.isEmpty()) {
            Log.w(LOGTAG, "IceStateChanged but SimlarCallState isEmpty");
            return;
        }

        Log.i(LOGTAG, "SimlarCallState IceStateChanged: " + mSimlarCallState);

        SimlarServiceBroadcast.sendSimlarCallStateChanged(this);
    }

    @Override
    public void onCallStateChanged(final String number, final State callState, final int msgId) {
        if (!mSimlarCallState.updateCallStateChanged(getContact(number).getNameOrNumber(), callState, msgId)) {
            Log.d(LOGTAG, "SimlarCallState staying the same: " + mSimlarCallState);
            return;
        }

        if (mSimlarCallState.isEmpty()) {
            Log.e(LOGTAG, "SimlarCallState is empty: " + mSimlarCallState);
            return;
        }

        Log.i(LOGTAG, "SimlarCallState updated: " + mSimlarCallState);

        if (mSimlarCallState.isRinging()) {
            mVibratorThread.start();
            mRingtoneThread.start();
        } else {
            mVibratorThread.stop();
            mRingtoneThread.stop();
        }

        // make sure WLAN is not suspended while calling
        if (mSimlarCallState.isNewCall()) {
            notifySimlarStatusChanged(SimlarStatus.ONGOING_CALL);

            acquireWakeLock();
            acquireWifiLock();

            if (((AudioManager) getSystemService(Context.AUDIO_SERVICE)).isMusicActive()) {
                sendBroadcast(new Intent("com.android.music.musicservicecommand.pause"));
                mResumeMusicAfterCall = true;
            }

            if (mSimlarCallState.isRinging()) {
                Log.i(LOGTAG, "starting RingingActivity");
                startActivity(new Intent(SimlarService.this, RingingActivity.class)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
            } else {
                Log.i(LOGTAG, "starting CallActivity");
                startActivity(new Intent(SimlarService.this, CallActivity.class)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));
            }
        }

        if (mSimlarCallState.isEndedCall()) {
            notifySimlarStatusChanged(SimlarStatus.ONLINE);

            releaseWakeLock();
            releaseWifiLock();

            if (mResumeMusicAfterCall) {
                sendBroadcast(new Intent("com.android.music.musicservicecommand.togglepause"));
                mResumeMusicAfterCall = false;
            }
        }

        SimlarServiceBroadcast.sendSimlarCallStateChanged(this);
    }

    @Override
    public void onPresenceStateChanged(final String number, final boolean online) {
        if (online) {
            Log.i(LOGTAG, "onPresenceStateChanged online " + number);
        }

        // we assume here that we only get the presence state of registered users
        if (updateContactData(number, online ? ContactStatus.ONLINE : ContactStatus.OFFLINE)) {
            if (online) {
                Log.i(LOGTAG, "notifyPresenceStateChanged online " + number);
            } else {
                Log.i(LOGTAG, "notifyPresenceStateChanged offline " + number);
            }

            SimlarServiceBroadcast.sendPresenceStateChanged(this, number, online);
        }
    }

    @Override
    public void onCallEncryptionChanged(final boolean encrypted, final String authenticationToken,
            final boolean authenticationTokenVerified) {
        if (!mSimlarCallState.updateCallEncryption(encrypted, authenticationToken, authenticationTokenVerified)) {
            Log.d(LOGTAG, "callEncryptionChanged but no difference in SimlarCallState: " + mSimlarCallState);
            return;
        }

        if (mSimlarCallState.isEmpty()) {
            Log.e(LOGTAG, "callEncryptionChanged but SimlarCallState isEmpty: ");
            return;
        }

        Log.i(LOGTAG, "SimlarCallState updated encryption: " + mSimlarCallState);
        SimlarServiceBroadcast.sendSimlarCallStateChanged(this);
    }

    public void call(final String simlarId) {
        mLinphoneThread.call(simlarId);
    }

    public void pickUp() {
        mLinphoneThread.pickUp();
    }

    public void terminateCall() {
        mLinphoneThread.terminateAllCalls();
    }

    public void terminate() {
        Log.i(LOGTAG, "terminate");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                handleTerminate();
            }
        });
    }

    void handleTerminate() {
        Log.i(LOGTAG, "handleTerminate");
        mGoingDown = true;
        SimlarServiceBroadcast.sendSimlarStatusChanged(this);

        if (mLinphoneThread != null && mSimlarStatus.isConnectedToSipServer()) {
            mLinphoneThread.unregister();

            // make sure terminatePrivate is called after at least 5 seconds
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    terminatePrivate();
                }
            }, 5000);
        } else {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    terminatePrivate();
                }
            });
        }
    }

    void terminatePrivate() {
        // make sure this function is only called once
        if (mTerminatePrivateAlreadyCalled) {
            Log.i(LOGTAG, "terminatePrivate already called");
            return;
        }
        mTerminatePrivateAlreadyCalled = true;

        Log.i(LOGTAG, "terminatePrivate");
        if (mLinphoneThread != null) {
            mLinphoneThread.finish();
        } else {
            onJoin();
        }
    }

    @Override
    public void onJoin() {
        try {
            mLinphoneThread.join(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SimlarServiceBroadcast.sendServiceFinishes(this);

        // make sure the notification update is done before destruction by firing destruction event to the handler
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i(LOGTAG, "terminatePrivate: calling stopSelf");
                stopForeground(true);
                stopSelf();
            }
        });
    }

    void loadContactsFromTelephonebook() {
        new AsyncTask<Void, Void, Map<String, ContactData>>() {
            @Override
            protected Map<String, ContactData> doInBackground(Void... params) {
                Log.i(LOGTAG, "loading contacts from telephone book");
                Map<String, ContactData> result = new HashMap<String, SimlarService.ContactData>();

                final String[] projection = new String[] { ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                        ContactsContract.CommonDataKinds.Phone.NUMBER,
                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.PHOTO_ID };

                final Cursor contacts = getContentResolver()
                        .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
                while (contacts.moveToNext()) {
                    final long contactId = contacts.getLong(0);
                    final String number = SimlarNumber.createSimlarNumber(contacts.getString(1));
                    final String name = contacts.getString(2);
                    final boolean hasPhotoId = contacts.getLong(3) != 0;
                    String photoUri = null;

                    if (Util.isNullOrEmpty(number)) {
                        continue;
                    }

                    if (hasPhotoId) {
                        Uri u = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
                        u = Uri.withAppendedPath(u, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
                        photoUri = u.toString();
                    }

                    if (!result.containsKey(number)) {
                        result.put(number, new ContactData(name, ContactStatus.UNKNOWN, photoUri));
                        Log.d(LOGTAG, "adding contact " + name + " " + number);
                    }
                }
                contacts.close();

                return result;
            }

            @Override
            protected void onPostExecute(Map<String, ContactData> result) {
                mContacts = result;
                requestRegisteredContacts();
            }
        }.execute();
    }

    @SuppressWarnings("unchecked")
    public void requestRegisteredContacts() {
        new AsyncTask<Set<String>, Void, Map<String, ContactStatus>>() {

            @Override
            protected Map<String, ContactStatus> doInBackground(Set<String>... params) {
                return GetContactsStatus.httpPostGetContactsStatus(params[0]);
            }

            @Override
            protected void onPostExecute(Map<String, ContactStatus> result) {
                if (result == null) {
                    Log.i(LOGTAG, "getting contacts status failed");
                    notifySimlarStatusChanged(SimlarStatus.ERROR_LOADING_CONTACTS);
                    return;
                }

                for (final String number : result.keySet()) {
                    final ContactStatus status = result.get(number);

                    if (!status.isRegistered()) {
                        updateContactData(number, status);
                    } else {
                        onPresenceStateChanged(number, status.isOnline());

                        // make sure to add friends in the gui thread
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                addLinphoneFriend(number);
                            }
                        });
                    }
                }

                if (mLinphoneThread.getRegistrationState() == RegistrationState.RegistrationOk) {
                    notifySimlarStatusChanged(SimlarStatus.ONLINE);
                }
            }
        }.execute(mContacts.keySet());
    }

    boolean updateContactData(final String number, final ContactStatus status) {
        if (Util.isNullOrEmpty(number)) {
            return false;
        }

        ContactData cd = mContacts.get(number);
        if (cd == null) {
            mContacts.put(number, new ContactData(null, status, null));
            return true;
        }

        if (cd.status == status) {
            return false;
        }

        cd.status = status;
        return true;
    }

    void addLinphoneFriend(final String number) {
        Log.d(LOGTAG, "adding linphone friend for presence watching: " + number);
        mLinphoneThread.addFriend(number);
    }

    public Set<FullContactData> getContacts() {
        Set<FullContactData> contacts = new HashSet<FullContactData>();
        for (final Map.Entry<String, ContactData> entry : mContacts.entrySet()) {
            if (entry.getValue().isRegistered()) {
                contacts.add(new FullContactData(entry.getKey(), entry.getValue()));
            }
        }
        return contacts;
    }

    public FullContactData getContact(final String number) {
        if (Util.isNullOrEmpty(number) || !mContacts.containsKey(number)) {
            return new FullContactData(number, "", ContactStatus.UNKNOWN, "");
        }

        return new FullContactData(number, mContacts.get(number));
    }

    public void verifyAuthenticationTokenOfCurrentCall(final boolean verified) {
        if (mLinphoneThread == null) {
            Log.e(LOGTAG, "ERROR: verifyAuthenticationToken called but no linphonehandler");
            return;
        }

        if (mSimlarCallState == null || Util.isNullOrEmpty(mSimlarCallState.getAuthenticationToken())) {
            Log.e(LOGTAG, "ERROR: verifyAuthenticationToken called but no token available");
            return;
        }

        mLinphoneThread.verifyAuthenticationToken(mSimlarCallState.getAuthenticationToken(), verified);
    }

    public SimlarStatus getSimlarStatus() {
        return mSimlarStatus;
    }

    public boolean isGoingDown() {
        return mGoingDown;
    }

    public SimlarCallState getSimlarCallState() {
        return mSimlarCallState;
    }

    public Volumes getVolumes() {
        return mLinphoneThread.getVolumes();
    }

    public void setVolumes(final Volumes volumes) {
        mLinphoneThread.setVolumes(volumes);
    }
}