com.csipsimple.service.SipService.java Source code

Java tutorial

Introduction

Here is the source code for com.csipsimple.service.SipService.java

Source

/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple 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.
 *
 *  CSipSimple 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 CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.csipsimple.service;

import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.SurfaceView;
import android.widget.Toast;

import com.csipsimple.R;
import com.csipsimple.api.ISipConfiguration;
import com.csipsimple.api.ISipService;
import com.csipsimple.api.MediaState;
import com.csipsimple.api.SipCallSession;
import com.csipsimple.api.SipConfigManager;
import com.csipsimple.api.SipManager;
import com.csipsimple.api.SipManager.PresenceStatus;
import com.csipsimple.api.SipMessage;
import com.csipsimple.api.SipProfile;
import com.csipsimple.api.SipProfileState;
import com.csipsimple.api.SipUri;
import com.csipsimple.db.DBProvider;
import com.csipsimple.http.HttpResponse;
import com.csipsimple.http.NetworkError;
import com.csipsimple.models.Filter;
import com.csipsimple.pjsip.PjSipCalls;
import com.csipsimple.pjsip.PjSipService;
import com.csipsimple.pjsip.UAStateReceiver;
import com.csipsimple.service.receiver.DynamicReceiver4;
import com.csipsimple.service.receiver.DynamicReceiver5;
import com.csipsimple.serviceutils.Constant;
import com.csipsimple.serviceutils.FileUtils;
import com.csipsimple.serviceutils.HttpUtil;
import com.csipsimple.serviceutils.ResHandler;
import com.csipsimple.serviceutils.ViewUtils;
import com.csipsimple.utils.Compatibility;
import com.csipsimple.utils.CustomDistribution;
import com.csipsimple.utils.ExtraPlugins;
import com.csipsimple.utils.ExtraPlugins.DynActivityPlugin;
import com.csipsimple.utils.Log;
import com.csipsimple.utils.PreferencesProviderWrapper;
import com.csipsimple.utils.PreferencesWrapper;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//import test.android.zkw.gittest.InCallMediaControl;

//import test.android.zkw.gittes.InCallMediaControl;

public class SipService extends Service {

    // static boolean creating = false;
    private static final String THIS_FILE = "SIP SRV";

    private SipWakeLock sipWakeLock;
    private boolean autoAcceptCurrent = false;
    public boolean supportMultipleCalls = false;

    // For video testing -- TODO : remove
    private static SipService singleton = null;

    // Implement public interface for the service
    private final ISipService.Stub binder = new ISipService.Stub() {

        /**
           * {@inheritDoc}
           */
        @Override
        public void sipStart() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "Start required from third party app/serv");
            getExecutor().execute(new StartRunnable());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void sipStop() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new StopRunnable());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void forceStopService() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "Try to force service stop");
            cleanStop();
            //stopSelf();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void askThreadedRestart() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "Restart required from third part app/serv");
            getExecutor().execute(new RestartRunnable());
        };

        /**
         * {@inheritDoc}
         */
        @Override
        public void addAllAccounts() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                public void doRun() throws SameThreadException {
                    SipService.this.addAllAccounts();
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void removeAllAccounts() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                public void doRun() throws SameThreadException {
                    SipService.this.unregisterAllAccounts(true);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void reAddAllAccounts() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                public void doRun() throws SameThreadException {
                    SipService.this.reAddAllAccounts();

                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setAccountRegistration(int accountId, int renew) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

            final SipProfile acc = getAccount(accountId);
            if (acc != null) {
                final int ren = renew;
                getExecutor().execute(new SipRunnable() {
                    @Override
                    public void doRun() throws SameThreadException {
                        SipService.this.setAccountRegistration(acc, ren, false);
                    }
                });
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public SipProfileState getSipProfileState(int accountId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            return SipService.this.getSipProfileState(accountId);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void switchToAutoAnswer() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "Switch to auto answer");
            setAutoAnswerNext(true);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void makeCall(final String callee, final int accountId) throws RemoteException {
            makeCallWithOptions(callee, accountId, null);
        }

        @Override
        public void makeCallWithOptions(final String callee, final int accountId, final Bundle options)
                throws RemoteException {

            Log.e("11-------", callee + "-----" + accountId);

            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            //We have to ensure service is properly started and not just binded
            SipService.this.startService(new Intent(SipService.this, SipService.class));

            if (pjService == null) {
                Log.e(THIS_FILE, "Can't place call if service not started");
                // TODO - we should return a failing status here
                return;
            }

            if (!supportMultipleCalls) {
                // Check if there is no ongoing calls if so drop this request by alerting user
                SipCallSession activeCall = pjService.getActiveCallInProgress();
                if (activeCall != null) {
                    if (!CustomDistribution.forceNoMultipleCalls()) {
                        notifyUserOfMessage(R.string.not_configured_multiple_calls);
                    }
                    return;
                }
            }

            Intent intent = new Intent(SipManager.ACTION_SIP_CALL_LAUNCH);
            intent.putExtra(SipProfile.FIELD_ID, accountId);
            intent.putExtra(SipManager.EXTRA_SIP_CALL_TARGET, callee);
            intent.putExtra(SipManager.EXTRA_SIP_CALL_OPTIONS, options);
            sendOrderedBroadcast(intent, SipManager.PERMISSION_USE_SIP, mPlaceCallResultReceiver, null,
                    Activity.RESULT_OK, null, null);

        }

        private BroadcastReceiver mPlaceCallResultReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, final Intent intent) {
                Log.d("checkIntent", "false:" + intent.getAction());

                final Bundle extras = intent.getExtras();
                final String action = intent.getAction();
                if (extras == null) {
                    Log.e(THIS_FILE, "No data in intent retrieved for call");
                    return;
                }
                if (!SipManager.ACTION_SIP_CALL_LAUNCH.equals(action)) {
                    Log.e(THIS_FILE, "Received invalid action " + action);
                    return;
                }

                android.util.Log.d(THIS_FILE, "check:" + action + "");

                final int accountId = extras.getInt(SipProfile.FIELD_ID, -2);
                final String callee = extras.getString(SipManager.EXTRA_SIP_CALL_TARGET);
                final Bundle options = extras.getBundle(SipManager.EXTRA_SIP_CALL_OPTIONS);
                if (accountId == -2 || callee == null) {
                    Log.e(THIS_FILE, "Invalid rewrite " + accountId);
                    return;
                }

                getExecutor().execute(new SipRunnable() {
                    @Override
                    protected void doRun() throws SameThreadException {
                        pjService.makeCall(callee, accountId, options);
                    }
                });
            }
        };

        /**
         * {@inheritDoc}
         */
        @Override
        public void sendMessage(final String message, final String callee, final long accountId)
                throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            //We have to ensure service is properly started and not just binded
            SipService.this.startService(new Intent(SipService.this, SipService.class));

            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    Log.d(THIS_FILE, "will sms " + callee);
                    if (pjService != null) {
                        ToCall called = pjService.sendMessage(callee, message, accountId);
                        if (called != null) {
                            SipMessage msg = new SipMessage(SipMessage.SELF, SipUri.getCanonicalSipContact(callee),
                                    SipUri.getCanonicalSipContact(called.getCallee()), message, "text/plain",
                                    System.currentTimeMillis(), SipMessage.MESSAGE_TYPE_QUEUED, called.getCallee());
                            msg.setRead(true);
                            getContentResolver().insert(SipMessage.MESSAGE_URI, msg.getContentValues());
                            Log.d(THIS_FILE, "Inserted " + msg.getTo());
                        } else {
                            SipService.this
                                    .notifyUserOfMessage(getString(R.string.invalid_sip_uri) + " : " + callee);
                        }
                    } else {
                        SipService.this.notifyUserOfMessage(getString(R.string.connection_not_valid));
                    }
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int answer(final int callId, final int status) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callAnswer(callId, status);
                }
            };
            getExecutor().execute(action);
            //return (Integer) action.getResult();
            return SipManager.SUCCESS;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hangup(final int callId, final int status) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callHangup(callId, status);
                }
            };
            getExecutor().execute(action);
            //return (Integer) action.getResult();

            return SipManager.SUCCESS;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int xfer(final int callId, final String callee) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "XFER");
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callXfer(callId, callee);
                }
            };
            getExecutor().execute(action);
            return (Integer) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int xferReplace(final int callId, final int otherCallId, final int options) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "XFER-replace");
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callXferReplace(callId, otherCallId, options);
                }
            };
            getExecutor().execute(action);
            return (Integer) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int sendDtmf(final int callId, final int keyCode) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.sendDtmf(callId, keyCode);
                }
            };
            getExecutor().execute(action);
            return (Integer) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int hold(final int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "HOLDING");
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callHold(callId);
                }
            };
            getExecutor().execute(action);
            return (Integer) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int reinvite(final int callId, final boolean unhold) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            Log.d(THIS_FILE, "REINVITING");
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Integer) pjService.callReinvite(callId, unhold);
                }
            };
            getExecutor().execute(action);
            return (Integer) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public SipCallSession getCallInfo(final int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            return new SipCallSession(pjService.getCallInfo(callId));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setBluetoothOn(final boolean on) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.setBluetoothOn(on);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setMicrophoneMute(final boolean on) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.setMicrophoneMute(on);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setSpeakerphoneOn(final boolean on) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.setSpeakerphoneOn(on);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public SipCallSession[] getCalls() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService != null) {
                SipCallSession[] listOfCallsImpl = pjService.getCalls();
                SipCallSession[] result = new SipCallSession[listOfCallsImpl.length];
                for (int sessIdx = 0; sessIdx < listOfCallsImpl.length; sessIdx++) {
                    result[sessIdx] = new SipCallSession(listOfCallsImpl[sessIdx]);
                }
                return result;
            }
            return new SipCallSession[0];
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void confAdjustTxLevel(final int port, final float value) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    if (pjService == null) {
                        return;
                    }
                    pjService.confAdjustTxLevel(port, value);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void confAdjustRxLevel(final int port, final float value) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    if (pjService == null) {
                        return;
                    }
                    pjService.confAdjustRxLevel(port, value);
                }
            });

        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void adjustVolume(SipCallSession callInfo, int direction, int flags) throws RemoteException {

            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

            if (pjService == null) {
                return;
            }

            boolean ringing = callInfo.isIncoming() && callInfo.isBeforeConfirmed();
            // Mode ringing
            if (ringing) {
                // What is expected here is to silence ringer
                //pjService.adjustStreamVolume(AudioManager.STREAM_RING, direction, AudioManager.FLAG_SHOW_UI);
                pjService.silenceRinger();
            } else {
                // Mode in call
                if (prefsWrapper.getPreferenceBooleanValue(SipConfigManager.USE_SOFT_VOLUME)) {
                    //Intent adjustVolumeIntent = new Intent(SipService.this, InCallMediaControl.class);
                    Intent adjustVolumeIntent = new Intent(SipService.this, null);
                    adjustVolumeIntent.putExtra(Intent.EXTRA_KEY_EVENT, direction);
                    adjustVolumeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(adjustVolumeIntent);
                } else {
                    pjService.adjustStreamVolume(
                            Compatibility.getInCallStream(pjService.mediaManager.doesUserWantBluetooth()),
                            direction, flags);
                }
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setEchoCancellation(final boolean on) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return;
            }
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.setEchoCancellation(on);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void startRecording(final int callId, final int way) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return;
            }
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.startRecording(callId, way);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void stopRecording(final int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return;
            }
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.stopRecording(callId);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isRecording(int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return false;
            }

            SipCallSession info = pjService.getCallInfo(callId);
            if (info != null) {
                return info.isRecording();
            }
            return false;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean canRecord(int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return false;
            }
            return pjService.canRecord(callId);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void playWaveFile(final String filePath, final int callId, final int way) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return;
            }
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.playWaveFile(filePath, callId, way);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void setPresence(final int presenceInt, final String statusText, final long accountId)
                throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            if (pjService == null) {
                return;
            }
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    presence = PresenceStatus.values()[presenceInt];
                    pjService.setPresence(presence, statusText, accountId);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int getPresence(long accountId) throws RemoteException {
            // TODO Auto-generated method stub
            return 0;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getPresenceStatus(long accountId) throws RemoteException {
            // TODO Auto-generated method stub
            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void zrtpSASVerified(final int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.zrtpSASVerified(callId);
                    pjService.zrtpReceiver.updateZrtpInfos(callId);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void zrtpSASRevoke(final int callId) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.zrtpSASRevoke(callId);
                    pjService.zrtpReceiver.updateZrtpInfos(callId);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public MediaState getCurrentMediaState() throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
            MediaState ms = new MediaState();
            if (pjService != null && pjService.mediaManager != null) {
                ms = pjService.mediaManager.getMediaState();
            }
            return ms;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int getVersion() throws RemoteException {
            return SipManager.CURRENT_API;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String showCallInfosDialog(final int callId) throws RemoteException {
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    String infos = PjSipCalls.dumpCallInfo(callId);
                    Log.d(THIS_FILE, infos);
                    return infos;
                }
            };

            getExecutor().execute(action);
            return (String) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int startLoopbackTest() throws RemoteException {
            if (pjService == null) {
                return SipManager.ERROR_CURRENT_NETWORK;
            }
            SipRunnable action = new SipRunnable() {

                @Override
                protected void doRun() throws SameThreadException {
                    pjService.startLoopbackTest();
                }
            };

            getExecutor().execute(action);
            return SipManager.SUCCESS;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int stopLoopbackTest() throws RemoteException {
            if (pjService == null) {
                return SipManager.ERROR_CURRENT_NETWORK;
            }
            SipRunnable action = new SipRunnable() {

                @Override
                protected void doRun() throws SameThreadException {
                    pjService.stopLoopbackTest();
                }
            };

            getExecutor().execute(action);
            return SipManager.SUCCESS;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public long confGetRxTxLevel(final int port) throws RemoteException {
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (Long) pjService.getRxTxLevel(port);
                }
            };
            getExecutor().execute(action);
            return (Long) action.getResult();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void ignoreNextOutgoingCallFor(String number) throws RemoteException {
            //      OutgoingCall.ignoreNext = number;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void updateCallOptions(final int callId, final Bundle options) throws RemoteException {
            getExecutor().execute(new SipRunnable() {
                @Override
                protected void doRun() throws SameThreadException {
                    pjService.updateCallOptions(callId, options);
                }
            });
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getLocalNatType() throws RemoteException {
            ReturnRunnable action = new ReturnRunnable() {
                @Override
                protected Object runWithReturn() throws SameThreadException {
                    return (String) pjService.getDetectedNatType();
                }
            };
            getExecutor().execute(action);
            return (String) action.getResult();
        }

    };

    private final ISipConfiguration.Stub binderConfiguration = new ISipConfiguration.Stub() {

        @Override
        public void setPreferenceBoolean(String key, boolean value) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_CONFIGURE_SIP, null);
            prefsWrapper.setPreferenceBooleanValue(key, value);
        }

        @Override
        public void setPreferenceFloat(String key, float value) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_CONFIGURE_SIP, null);
            prefsWrapper.setPreferenceFloatValue(key, value);

        }

        @Override
        public void setPreferenceString(String key, String value) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_CONFIGURE_SIP, null);
            prefsWrapper.setPreferenceStringValue(key, value);

        }

        @Override
        public String getPreferenceString(String key) throws RemoteException {
            return prefsWrapper.getPreferenceStringValue(key);

        }

        @Override
        public boolean getPreferenceBoolean(String key) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_CONFIGURE_SIP, null);
            return prefsWrapper.getPreferenceBooleanValue(key);

        }

        @Override
        public float getPreferenceFloat(String key) throws RemoteException {
            SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_CONFIGURE_SIP, null);
            return prefsWrapper.getPreferenceFloatValue(key);
        }

    };

    private WakeLock wakeLock;
    private WifiLock wifiLock;
    private DynamicReceiver4 deviceStateReceiver;
    private PreferencesProviderWrapper prefsWrapper;
    private ServicePhoneStateReceiver phoneConnectivityReceiver;
    private TelephonyManager telephonyManager;
    //   private ConnectivityManager connectivityManager;

    public SipNotifications notificationManager;
    private SipServiceExecutor mExecutor;
    private static PjSipService pjService;
    private static HandlerThread executorThread;

    private AccountStatusContentObserver statusObserver = null;
    //public PresenceManager presenceMgr;
    private BroadcastReceiver serviceReceiver;

    class AccountStatusContentObserver extends ContentObserver {
        public AccountStatusContentObserver(Handler h) {
            super(h);
        }

        public void onChange(boolean selfChange) {
            Log.d(THIS_FILE, "Accounts status.onChange( " + selfChange + ")");
            updateRegistrationsState();
        }
    }

    public SipServiceExecutor getExecutor() {
        // create mExecutor lazily
        if (mExecutor == null) {
            mExecutor = new SipServiceExecutor(this);
        }
        return mExecutor;
    }

    private class ServicePhoneStateReceiver extends PhoneStateListener {

        //private boolean ignoreFirstConnectionState = true;
        private boolean ignoreFirstCallState = true;
        /*
        @Override
        public void onDataConnectionStateChanged(final int state) {
           if(!ignoreFirstConnectionState) {
        Log.d(THIS_FILE, "Data connection state changed : " + state);
        Thread t = new Thread("DataConnectionDetach") {
           @Override
           public void run() {
              if(deviceStateReceiver != null) {
                 deviceStateReceiver.onChanged("MOBILE", state == TelephonyManager.DATA_CONNECTED);
              }
           }
        };
        t.start();
           }else {
        ignoreFirstConnectionState = false;
           }
           super.onDataConnectionStateChanged(state);
        }
        */

        @Override
        public void onCallStateChanged(final int state, final String incomingNumber) {
            if (!ignoreFirstCallState) {
                Log.d(THIS_FILE, "Call state has changed !" + state + " : " + incomingNumber);
                getExecutor().execute(new SipRunnable() {

                    @Override
                    protected void doRun() throws SameThreadException {
                        if (pjService != null) {
                            pjService.onGSMStateChanged(state, incomingNumber);
                        }
                    }
                });
            } else {
                ignoreFirstCallState = false;
            }
            super.onCallStateChanged(state, incomingNumber);
        }
    }

    //   private boolean flag=false;
    private Notification notification = null;

    @Override
    public void onCreate() {
        Log.d("caijun", "service oncreate");
        super.onCreate();
        singleton = this;
        //
        //      Thread onlineThread = new Thread()
        //      {
        //         @Override
        //         public void run() {
        //            try {
        //               while (true) {
        //                  android.util.Log.d("checkOnLine",flag+"");
        //                  if (flag&& ViewUtils.isWifiOn()) {
        //                     HttpUtil.httpGet(context, Constant.SERVERHOST + "config/check-heart-online?access-token=" + FileUtils.getShareString("token",context), null, resHandler);
        //                  }
        //                  Thread.sleep(30000);
        //               }
        //            } catch (Exception e)
        //            {
        //               e.printStackTrace();
        //            }
        //         }
        //      };
        //
        //      onlineThread.start();

        Log.i(THIS_FILE, "Create SIP Service");
        prefsWrapper = new PreferencesProviderWrapper(this);
        Log.setLogLevel(prefsWrapper.getLogLevel());

        telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        //      connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        notificationManager = new SipNotifications(this);
        notificationManager.onServiceCreate();
        sipWakeLock = new SipWakeLock((PowerManager) getSystemService(Context.POWER_SERVICE));

        boolean hasSetup = prefsWrapper
                .getPreferenceBooleanValue(PreferencesProviderWrapper.HAS_ALREADY_SETUP_SERVICE, false);
        Log.d(THIS_FILE, "Service has been setup ? " + hasSetup);

        //presenceMgr = new PresenceManager();
        registerServiceBroadcasts();

        if (!hasSetup) {
            Log.e(THIS_FILE, "RESET SETTINGS !!!!");
            prefsWrapper.resetAllDefaultValues();
        }

    }

    private static Class cl;

    public static void setClass(Class c) {
        cl = c;
    }

    private static Context context;

    public static void setContext(Context c1) {
        context = c1;
    }

    public static Context getContext() {
        return context;
    }

    private int notifyId = 50;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        android.util.Log.d("checkStart", "onStartCommand");
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        PendingIntent pendingintent = PendingIntent.getActivity(context, 0, new Intent(context, cl), 0);
        notification = builder.build();
        notification.setLatestEventInfo(this, "uploadservice", "??????", pendingintent);

        startForeground(notifyId, notification);

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(THIS_FILE, "Destroying SIP Service");
        unregisterBroadcasts();
        unregisterServiceBroadcasts();
        notificationManager.onServiceDestroy();
        getExecutor().execute(new FinalizeDestroyRunnable());
        stopForeground(true);
    }

    public void cleanStop() {
        getExecutor().execute(new DestroyRunnable());
    }

    private void applyComponentEnablingState(boolean active) {
        int enableState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        if (active && prefsWrapper.getPreferenceBooleanValue(SipConfigManager.INTEGRATE_TEL_PRIVILEGED)) {
            // Check whether we should register for stock tel: intents
            // Useful for devices without gsm
            enableState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
        }
        PackageManager pm = getPackageManager();

        ComponentName cmp = new ComponentName(this, "com.csipsimple.ui.PrivilegedOutgoingCallBroadcaster");
        try {
            if (pm.getComponentEnabledSetting(cmp) != enableState) {
                pm.setComponentEnabledSetting(cmp, enableState, PackageManager.DONT_KILL_APP);
            }
        } catch (IllegalArgumentException e) {
            Log.d(THIS_FILE,
                    "Current manifest has no PrivilegedOutgoingCallBroadcaster -- you can ignore this if voluntary",
                    e);
        }
    }

    private void registerServiceBroadcasts() {
        if (serviceReceiver == null) {
            IntentFilter intentfilter = new IntentFilter();
            intentfilter.addAction(SipManager.ACTION_DEFER_OUTGOING_UNREGISTER);
            intentfilter.addAction(SipManager.ACTION_OUTGOING_UNREGISTER);
            serviceReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Log.d("checkIntent", "intent:" + intent.getAction());
                    String action = intent.getAction();
                    if (action.equals(SipManager.ACTION_OUTGOING_UNREGISTER)) {
                        unregisterForOutgoing(
                                (ComponentName) intent.getParcelableExtra(SipManager.EXTRA_OUTGOING_ACTIVITY));
                    } else if (action.equals(SipManager.ACTION_DEFER_OUTGOING_UNREGISTER)) {
                        deferUnregisterForOutgoing(
                                (ComponentName) intent.getParcelableExtra(SipManager.EXTRA_OUTGOING_ACTIVITY));
                    }
                }

            };
            registerReceiver(serviceReceiver, intentfilter);
        }
    }

    private void unregisterServiceBroadcasts() {
        if (serviceReceiver != null) {
            unregisterReceiver(serviceReceiver);
            serviceReceiver = null;
        }
    }

    /**
     * Register broadcast receivers.
     */
    private void registerBroadcasts() {
        // Register own broadcast receiver
        if (deviceStateReceiver == null) {
            IntentFilter intentfilter = new IntentFilter();
            intentfilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
            intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_CHANGED);
            intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_DELETED);
            intentfilter.addAction(SipManager.ACTION_SIP_CAN_BE_STOPPED);
            intentfilter.addAction(SipManager.ACTION_SIP_REQUEST_RESTART);
            intentfilter.addAction(DynamicReceiver4.ACTION_VPN_CONNECTIVITY);
            if (Compatibility.isCompatible(5)) {
                deviceStateReceiver = new DynamicReceiver5(this);
            } else {
                deviceStateReceiver = new DynamicReceiver4(this);
            }
            registerReceiver(deviceStateReceiver, intentfilter);
            deviceStateReceiver.startMonitoring();
        }
        // Telephony
        if (phoneConnectivityReceiver == null) {
            Log.d(THIS_FILE, "Listen for phone state ");
            phoneConnectivityReceiver = new ServicePhoneStateReceiver();

            telephonyManager.listen(phoneConnectivityReceiver, /*PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
                                                               | */PhoneStateListener.LISTEN_CALL_STATE);
        }
        // Content observer
        if (statusObserver == null) {
            statusObserver = new AccountStatusContentObserver(serviceHandler);
            getContentResolver().registerContentObserver(SipProfile.ACCOUNT_STATUS_URI, true, statusObserver);
        }

    }

    /**
     * Remove registration of broadcasts receiver.
     */
    private void unregisterBroadcasts() {
        if (deviceStateReceiver != null) {
            try {
                Log.d(THIS_FILE, "Stop and unregister device receiver");
                deviceStateReceiver.stopMonitoring();
                unregisterReceiver(deviceStateReceiver);
                deviceStateReceiver = null;
            } catch (IllegalArgumentException e) {
                // This is the case if already unregistered itself
                // Python style usage of try ;) : nothing to do here since it could
                // be a standard case
                // And in this case nothing has to be done
                Log.d(THIS_FILE, "Has not to unregister telephony receiver");
            }
        }
        if (phoneConnectivityReceiver != null) {
            Log.d(THIS_FILE, "Unregister telephony receiver");
            telephonyManager.listen(phoneConnectivityReceiver, PhoneStateListener.LISTEN_NONE);
            phoneConnectivityReceiver = null;
        }
        if (statusObserver != null) {
            getContentResolver().unregisterContentObserver(statusObserver);
            statusObserver = null;
        }

    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("deprecation")
    @Override
    public void onStart(Intent intent, int startId) {
        Log.d("caijun", "service onstart");
        android.util.Log.d("checkStart", "onStart");
        super.onStart(intent, startId);
        if (intent != null) {
            Parcelable p = intent.getParcelableExtra(SipManager.EXTRA_OUTGOING_ACTIVITY);
            if (p != null) {
                ComponentName outActivity = (ComponentName) p;
                registerForOutgoing(outActivity);
            }
        }

        // Check connectivity, else just finish itself
        if (!isConnectivityValid()) {
            notifyUserOfMessage(R.string.connection_not_valid);
            Log.d(THIS_FILE, "Harakiri... we are not needed since no way to use self");
            cleanStop();
            return;
        }

        // Autostart the stack - make sure started and that receivers are there
        // NOTE : the stack may also be autostarted cause of phoneConnectivityReceiver
        if (!loadStack()) {
            return;
        }

        //if(directConnect) {
        Log.d(THIS_FILE, "Direct sip start");
        getExecutor().execute(new StartRunnable());
        /*
        }else {
        Log.d(THIS_FILE, "Defered SIP start !!");
        NetworkInfo netInfo = (NetworkInfo) connectivityManager.getActiveNetworkInfo();
        if(netInfo != null) {
        String type = netInfo.getTypeName();
        NetworkInfo.State state = netInfo.getState();
        if(state == NetworkInfo.State.CONNECTED) {
           Log.d(THIS_FILE, ">> on changed connected");
           deviceStateReceiver.onChanged(type, true);
        }else if(state == NetworkInfo.State.DISCONNECTED) {
           Log.d(THIS_FILE, ">> on changed disconnected");
           deviceStateReceiver.onChanged(type, false);
        }
        }else {
        deviceStateReceiver.onChanged(null, false);
        Log.d(THIS_FILE, ">> on changed disconnected");
        }
        }
        */
    }

    private List<ComponentName> activitiesForOutgoing = new ArrayList<ComponentName>();
    private List<ComponentName> deferedUnregisterForOutgoing = new ArrayList<ComponentName>();

    public void registerForOutgoing(ComponentName activityKey) {
        if (!activitiesForOutgoing.contains(activityKey)) {
            activitiesForOutgoing.add(activityKey);
        }
    }

    public void unregisterForOutgoing(ComponentName activityKey) {
        activitiesForOutgoing.remove(activityKey);

        if (!isConnectivityValid()) {
            cleanStop();
        }
    }

    public void deferUnregisterForOutgoing(ComponentName activityKey) {
        if (!deferedUnregisterForOutgoing.contains(activityKey)) {
            deferedUnregisterForOutgoing.add(activityKey);
        }
    }

    public void treatDeferUnregistersForOutgoing() {
        for (ComponentName cmp : deferedUnregisterForOutgoing) {
            activitiesForOutgoing.remove(cmp);
        }
        deferedUnregisterForOutgoing.clear();
        if (!isConnectivityValid()) {
            cleanStop();
        }
    }

    public boolean isConnectivityValid() {
        if (prefsWrapper.getPreferenceBooleanValue(PreferencesWrapper.HAS_BEEN_QUIT, false)) {
            return false;
        }
        boolean valid = prefsWrapper.isValidConnectionForIncoming();
        if (activitiesForOutgoing.size() > 0) {
            valid |= prefsWrapper.isValidConnectionForOutgoing();
        }
        return valid;
    }

    private boolean loadStack() {
        //Ensure pjService exists
        if (pjService == null) {
            pjService = new PjSipService();
        }
        pjService.setService(this);

        if (pjService.tryToLoadStack()) {
            return true;
        }
        return false;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d("caijun", "service onBind");
        String serviceName = intent.getAction();
        Log.d(THIS_FILE, "Action is " + serviceName);
        Log.d("checkSip", serviceName + "");
        if (serviceName == null || serviceName.equalsIgnoreCase(SipManager.INTENT_SIP_SERVICE)) {
            Log.d(THIS_FILE, "Service returned");
            return binder;
        } else if (serviceName.equalsIgnoreCase(SipManager.INTENT_SIP_CONFIGURATION)) {
            Log.d(THIS_FILE, "Conf returned");
            return binderConfiguration;
        }
        //      if(serviceName.equalsIgnoreCase(BINDERNAME)){
        //         Log.d(THIS_FILE, "check");
        //         return nnder;
        //      }

        Log.d(THIS_FILE, "Default service (SipService) returned");
        return binder;
    }

    //private KeepAliveTimer kaAlarm;
    // This is always done in SipExecutor thread
    private void startSipStack() throws SameThreadException {
        //Cache some prefs
        supportMultipleCalls = prefsWrapper.getPreferenceBooleanValue(SipConfigManager.SUPPORT_MULTIPLE_CALLS);

        if (!isConnectivityValid()) {
            notifyUserOfMessage(R.string.connection_not_valid);
            Log.e(THIS_FILE, "No need to start sip");
            return;
        }
        Log.d(THIS_FILE, "Start was asked and we should actually start now");
        if (pjService == null) {
            Log.d(THIS_FILE, "Start was asked and pjService in not there");
            if (!loadStack()) {
                Log.e(THIS_FILE, "Unable to load SIP stack !! ");
                return;
            }
        }
        Log.d(THIS_FILE, "Ask pjservice to start itself");

        //presenceMgr.startMonitoring(this);
        boolean flag = false;
        Log.d("caijun", "start register");
        if (flag = pjService.sipStart()) {
            // This should be done after in acquire resource
            // But due to http://code.google.com/p/android/issues/detail?id=21635
            // not a good idea
            applyComponentEnablingState(true);
            Log.d("caijun", flag + "");
            registerBroadcasts();
            Log.d(THIS_FILE, "Add all accounts");
            addAllAccounts();
        }
        //      addAllAccounts();
        //addAllAccounts();
    }

    /**
     * Safe stop the sip stack
     * @return true if can be stopped, false if there is a pending call and the sip service should not be stopped
     */
    public boolean stopSipStack() throws SameThreadException {
        Log.d(THIS_FILE, "Stop sip stack");
        boolean canStop = true;
        if (pjService != null) {
            canStop &= pjService.sipStop();
            /*
            if(canStop) {
               pjService = null;
            }
            */
        }
        if (canStop) {
            //if(presenceMgr != null) {
            //    presenceMgr.stopMonitoring();
            //}

            // Due to http://code.google.com/p/android/issues/detail?id=21635
            // exclude 14 and upper from auto disabling on stop.
            if (!Compatibility.isCompatible(14)) {
                applyComponentEnablingState(false);
            }

            unregisterBroadcasts();
            releaseResources();
        }

        return canStop;
    }

    public void restartSipStack() throws SameThreadException {
        if (stopSipStack()) {
            startSipStack();
        } else {
            Log.e(THIS_FILE, "Can't stop ... so do not restart ! ");
        }
    }

    /**
     * Notify user from a message the sip stack wants to transmit.
     * For now it shows a toaster
     * @param msg String message to display
     */
    public void notifyUserOfMessage(String msg) {
        serviceHandler.sendMessage(serviceHandler.obtainMessage(TOAST_MESSAGE, msg));
    }

    /**
     * Notify user from a message the sip stack wants to transmit.
      * For now it shows a toaster
     * @param resStringId The id of the string resource to display
     */
    public void notifyUserOfMessage(int resStringId) {
        serviceHandler.sendMessage(serviceHandler.obtainMessage(TOAST_MESSAGE, resStringId, 0));
    }

    private boolean hasSomeActiveAccount = false;

    /**
     * Add accounts from database
     */
    private void addAllAccounts() throws SameThreadException {
        Log.d(THIS_FILE, "We are adding all accounts right now....");
        SipProfile account = null;
        boolean hasSomeSuccess = false;
        Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION,
                SipProfile.FIELD_ACTIVE + "=?", new String[] { "1" }, null);
        if (c != null) {
            try {
                int index = 0;
                if (c.getCount() > 0) {
                    c.moveToFirst();
                    do {
                        account = new SipProfile(c);
                        Log.d("caijun", "here");
                        Log.d("caijun", pjService == null ? "null" : "not null");
                        if (pjService != null && pjService.addAccount(account)) {
                            hasSomeSuccess = true;

                        }
                        index++;
                    } while (c.moveToNext() && index < 10);
                }
            } catch (Exception e) {
                Log.e(THIS_FILE, "Error on looping over sip profiles", e);
            } finally {
                c.close();
            }
        }

        hasSomeActiveAccount = hasSomeSuccess;

        if (hasSomeSuccess) {
            acquireResources();

        } else {
            releaseResources();
            if (notificationManager != null) {
                notificationManager.cancelRegisters();
            }
        }
        //      Log.d("caijun","sip ?"+account.getSipUserName());
        //      sendBroadcast(new Intent(SipManager.REGISTER_SUCCESS));
    }

    public boolean setAccountRegistration(SipProfile account, int renew, boolean forceReAdd)
            throws SameThreadException {
        boolean status = false;
        if (pjService != null) {
            status = pjService.setAccountRegistration(account, renew, forceReAdd);
        }

        return status;
    }

    /**
     * Remove accounts from database
     */
    private void unregisterAllAccounts(boolean cancelNotification) throws SameThreadException {

        releaseResources();

        Log.d(THIS_FILE, "Remove all accounts");

        Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION, null,
                null, null);
        if (c != null) {
            try {
                c.moveToFirst();
                do {
                    SipProfile account = new SipProfile(c);
                    setAccountRegistration(account, 0, false);
                } while (c.moveToNext());
            } catch (Exception e) {
                Log.e(THIS_FILE, "Error on looping over sip profiles", e);
            } finally {
                c.close();
            }
        }

        if (notificationManager != null && cancelNotification) {
            notificationManager.cancelRegisters();
        }
    }

    private void reAddAllAccounts() throws SameThreadException {
        Log.d(THIS_FILE, "RE REGISTER ALL ACCOUNTS");
        unregisterAllAccounts(false);
        addAllAccounts();
    }

    public SipProfileState getSipProfileState(int accountDbId) {
        final SipProfile acc = getAccount(accountDbId);
        if (pjService != null && acc != null) {
            return pjService.getProfileState(acc);
        }
        return null;
    }

    public void updateRegistrationsState() {
        Log.d(THIS_FILE, "Update registration state");
        ArrayList<SipProfileState> activeProfilesState = new ArrayList<SipProfileState>();
        Cursor c = getContentResolver().query(SipProfile.ACCOUNT_STATUS_URI, null, null, null, null);
        if (c != null) {
            try {
                if (c.getCount() > 0) {
                    c.moveToFirst();
                    do {
                        SipProfileState ps = new SipProfileState(c);
                        if (ps.isValidForCall()) {
                            activeProfilesState.add(ps);
                        }
                    } while (c.moveToNext());
                }
            } catch (Exception e) {
                Log.e(THIS_FILE, "Error on looping over sip profiles", e);
            } finally {
                c.close();
            }
        }

        Collections.sort(activeProfilesState, SipProfileState.getComparator());

        // Handle status bar notification
        if (activeProfilesState.size() > 0
                && prefsWrapper.getPreferenceBooleanValue(SipConfigManager.ICON_IN_STATUS_BAR, true)) {
            // Testing memory / CPU leak as per issue 676
            //   for(int i=0; i < 10; i++) {
            //      Log.d(THIS_FILE, "Notify ...");
            notificationManager.notifyRegisteredAccounts(activeProfilesState,
                    prefsWrapper.getPreferenceBooleanValue(SipConfigManager.ICON_IN_STATUS_BAR_NBR));
            //      try {
            //         Thread.sleep(6000);
            //      } catch (InterruptedException e) {
            //         e.printStackTrace();
            //      }
            //   }
        } else {
            notificationManager.cancelRegisters();
        }

        if (hasSomeActiveAccount) {
            acquireResources();
        } else {
            releaseResources();
        }
        //      Log.d("caijun","sip ?"+account.getSipUserName());
        //      sendBroadcast(new Intent(SipManager.REGISTER_SUCCESS));
    }

    /**
     * Get the currently instanciated prefsWrapper (to be used by
     * UAStateReceiver)
     * 
     * @return the preferenceWrapper instanciated
     */
    public PreferencesProviderWrapper getPrefs() {
        // Is never null when call so ok, just not check...
        return prefsWrapper;
    }

    //Binders for media manager to sip stack
    /**
     * Adjust tx software sound level
     * @param speakVolume volume 0.0 - 1.0
     */
    public void confAdjustTxLevel(float speakVolume) throws SameThreadException {
        if (pjService != null) {
            pjService.confAdjustTxLevel(0, speakVolume);
        }
    }

    /**
     * Adjust rx software sound level
     * @param speakVolume volume 0.0 - 1.0
     */
    public void confAdjustRxLevel(float speakVolume) throws SameThreadException {
        if (pjService != null) {
            pjService.confAdjustRxLevel(0, speakVolume);
        }
    }

    /**
     * Add a buddy to list 
     * @param buddyUri the sip uri of the buddy to add
     */
    public int addBuddy(String buddyUri) throws SameThreadException {
        int retVal = -1;
        if (pjService != null) {
            Log.d(THIS_FILE, "Trying to add buddy " + buddyUri);
            retVal = pjService.addBuddy(buddyUri);
        }
        return retVal;
    }

    /**
     * Remove a buddy from buddies
     * @param buddyUri the sip uri of the buddy to remove
     */
    public void removeBuddy(String buddyUri) throws SameThreadException {
        if (pjService != null) {
            pjService.removeBuddy(buddyUri);
        }
    }

    private boolean holdResources = false;

    /**
     * Ask to take the control of the wifi and the partial wake lock if
     * configured
     */
    private synchronized void acquireResources() {
        if (holdResources) {
            return;
        }

        // Add a wake lock for CPU if necessary
        if (prefsWrapper.getPreferenceBooleanValue(SipConfigManager.USE_PARTIAL_WAKE_LOCK)) {
            PowerManager pman = (PowerManager) getSystemService(Context.POWER_SERVICE);
            if (wakeLock == null) {
                wakeLock = pman.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "com.csipsimple.SipService");
                wakeLock.setReferenceCounted(false);
            }
            // Extra check if set reference counted is false ???
            if (!wakeLock.isHeld()) {
                wakeLock.acquire();
            }
        }

        // Add a lock for WIFI if necessary
        WifiManager wman = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        if (wifiLock == null) {
            int mode = WifiManager.WIFI_MODE_FULL;
            if (Compatibility.isCompatible(9)
                    && prefsWrapper.getPreferenceBooleanValue(SipConfigManager.LOCK_WIFI_PERFS)) {
                mode = 0x3; // WIFI_MODE_FULL_HIGH_PERF 
            }
            wifiLock = wman.createWifiLock(mode, "com.csipsimple.SipService");
            wifiLock.setReferenceCounted(false);
        }
        if (prefsWrapper.getPreferenceBooleanValue(SipConfigManager.LOCK_WIFI) && !wifiLock.isHeld()) {
            WifiInfo winfo = wman.getConnectionInfo();
            if (winfo != null) {
                DetailedState dstate = WifiInfo.getDetailedStateOf(winfo.getSupplicantState());
                // We assume that if obtaining ip addr, we are almost connected
                // so can keep wifi lock
                if (dstate == DetailedState.OBTAINING_IPADDR || dstate == DetailedState.CONNECTED) {
                    if (!wifiLock.isHeld()) {
                        wifiLock.acquire();
                    }
                }
            }
        }
        holdResources = true;
    }

    private synchronized void releaseResources() {
        if (wakeLock != null && wakeLock.isHeld()) {
            wakeLock.release();
        }
        if (wifiLock != null && wifiLock.isHeld()) {
            wifiLock.release();
        }
        holdResources = false;
    }

    private static final int TOAST_MESSAGE = 0;

    private Handler serviceHandler = new ServiceHandler(this);

    private static class ServiceHandler extends Handler {
        WeakReference<SipService> s;

        public ServiceHandler(SipService sipService) {
            s = new WeakReference<SipService>(sipService);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            SipService sipService = s.get();
            if (sipService == null) {
                return;
            }
            if (msg.what == TOAST_MESSAGE) {
                if (msg.arg1 != 0) {
                    Toast.makeText(sipService, msg.arg1, Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(sipService, (String) msg.obj, Toast.LENGTH_LONG).show();
                }
            }
        }
    };

    public UAStateReceiver getUAStateReceiver() {
        return pjService.userAgentReceiver;
    }

    public int getGSMCallState() {
        return telephonyManager.getCallState();
    }

    public static final class ToCall {
        private Integer pjsipAccountId;
        private String callee;
        private String dtmf;

        public ToCall(Integer acc, String uri) {
            pjsipAccountId = acc;
            callee = uri;
        }

        public ToCall(Integer acc, String uri, String dtmfChars) {
            pjsipAccountId = acc;
            callee = uri;
            dtmf = dtmfChars;
        }

        /**
         * @return the pjsipAccountId
         */
        public Integer getPjsipAccountId() {
            return pjsipAccountId;
        }

        /**
         * @return the callee
         */
        public String getCallee() {
            return callee;
        }

        /**
         * @return the dtmf sequence to automatically dial for this call
         */
        public String getDtmf() {
            return dtmf;
        }
    };

    public SipProfile getAccount(long accountId) {
        // TODO : create cache at this point to not requery each time as far as it's a service query
        return SipProfile.getProfileFromDbId(this, accountId, DBProvider.ACCOUNT_FULL_PROJECTION);
    }

    // Auto answer feature

    public void setAutoAnswerNext(boolean auto_response) {
        autoAcceptCurrent = auto_response;
    }

    /**
     * Should a current incoming call be answered.
     * A call to this method will reset internal state
     * @param remContact The remote contact to test
     * @param acc The incoming guessed account
     * @return the sip code to auto-answer with. If > 0 it means that an auto answer must be fired
     */
    public int shouldAutoAnswer(String remContact, SipProfile acc, Bundle extraHdr) {

        Log.d(THIS_FILE, "Search if should I auto answer for " + remContact);
        int shouldAutoAnswer = 0;

        if (autoAcceptCurrent) {
            Log.d(THIS_FILE, "I should auto answer this one !!! ");
            autoAcceptCurrent = false;
            return 200;
        }

        if (acc != null) {
            Pattern p = Pattern.compile("^(?:\")?([^<\"]*)(?:\")?[ ]*(?:<)?sip(?:s)?:([^@]*@[^>]*)(?:>)?",
                    Pattern.CASE_INSENSITIVE);
            Matcher m = p.matcher(remContact);
            String number = remContact;
            if (m.matches()) {
                number = m.group(2);
            }
            shouldAutoAnswer = Filter.isAutoAnswerNumber(this, acc.id, number, extraHdr);

        } else {
            Log.e(THIS_FILE, "Oupps... that come from an unknown account...");
            // If some user need to auto hangup if comes from unknown account, just needed to add local account and filter on it.
        }
        return shouldAutoAnswer;
    }

    // Media direct binders
    public void setNoSnd() throws SameThreadException {
        if (pjService != null) {
            pjService.setNoSnd();
        }
    }

    public void setSnd() throws SameThreadException {
        if (pjService != null) {
            pjService.setSnd();
        }
    }

    private static Looper createLooper() {
        //   synchronized (executorThread) {
        if (executorThread == null) {
            Log.d(THIS_FILE, "Creating new handler thread");
            // ADT gives a fake warning due to bad parse rule.
            executorThread = new HandlerThread("SipService.Executor");
            executorThread.start();
        }
        //   }
        return executorThread.getLooper();
    }

    // Executes immediate tasks in a single executorThread.
    // Hold/release wake lock for running tasks
    public static class SipServiceExecutor extends Handler {
        WeakReference<SipService> handlerService;

        SipServiceExecutor(SipService s) {
            super(createLooper());
            handlerService = new WeakReference<SipService>(s);
        }

        public void execute(Runnable task) {
            SipService s = handlerService.get();
            if (s != null) {
                s.sipWakeLock.acquire(task);
            }
            Message.obtain(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(THIS_FILE, "can't handle msg: " + msg);
            }
        }

        private void executeInternal(Runnable task) {
            try {
                task.run();
            } catch (Throwable t) {
                Log.e(THIS_FILE, "run task: " + task, t);
            } finally {

                SipService s = handlerService.get();
                if (s != null) {
                    s.sipWakeLock.release(task);
                }
            }
        }
    }

    class StartRunnable extends SipRunnable {
        @Override
        protected void doRun() throws SameThreadException {
            startSipStack();
        }
    }

    class SyncStartRunnable extends ReturnRunnable {
        @Override
        protected Object runWithReturn() throws SameThreadException {
            startSipStack();
            return null;
        }
    }

    class StopRunnable extends SipRunnable {
        @Override
        protected void doRun() throws SameThreadException {
            stopSipStack();
        }
    }

    class SyncStopRunnable extends ReturnRunnable {
        @Override
        protected Object runWithReturn() throws SameThreadException {
            stopSipStack();
            return null;
        }
    }

    class RestartRunnable extends SipRunnable {
        @Override
        protected void doRun() throws SameThreadException {
            if (stopSipStack()) {
                startSipStack();
            } else {
                Log.e(THIS_FILE, "Can't stop ... so do not restart ! ");
            }
        }
    }

    class SyncRestartRunnable extends ReturnRunnable {
        @Override
        protected Object runWithReturn() throws SameThreadException {
            if (stopSipStack()) {
                startSipStack();
            } else {
                Log.e(THIS_FILE, "Can't stop ... so do not restart ! ");
            }
            return null;
        }
    }

    class DestroyRunnable extends SipRunnable {
        @Override
        protected void doRun() throws SameThreadException {
            if (stopSipStack()) {
                stopSelf();
            }
        }
    }

    class FinalizeDestroyRunnable extends SipRunnable {
        @Override
        protected void doRun() throws SameThreadException {

            mExecutor = null;

            Log.d(THIS_FILE, "Destroy sip stack");

            sipWakeLock.reset();

            if (stopSipStack()) {
                notificationManager.cancelAll();
                notificationManager.cancelCalls();
            } else {
                Log.e(THIS_FILE, "Somebody has stopped the service while there is an ongoing call !!!");
            }
            /* If we activate that we can get two concurrent executorThread 
            synchronized (executorThread) {
               HandlerThread currentHandlerThread = executorThread;
               executorThread = null;
               System.gc();
               // This is a little bit crappy, we are cutting were we sit.
               Threading.stopHandlerThread(currentHandlerThread, false);
            }
            */

            // We will not go longer
            Log.i(THIS_FILE, "--- SIP SERVICE DESTROYED ---");
        }
    }

    // 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;

        public void run() {
            try {
                doRun();
            } catch (SameThreadException e) {
                Log.e(THIS_FILE, "Not done from same thread");
            }
        }
    }

    public abstract class ReturnRunnable extends SipRunnable {
        private Semaphore runSemaphore;
        private Object resultObject;

        public ReturnRunnable() {
            super();
            runSemaphore = new Semaphore(0);
        }

        public Object getResult() {
            try {
                runSemaphore.acquire();
            } catch (InterruptedException e) {
                Log.e(THIS_FILE, "Can't acquire run semaphore... problem...");
            }
            return resultObject;
        }

        protected abstract Object runWithReturn() throws SameThreadException;

        @Override
        public void doRun() throws SameThreadException {
            setResult(runWithReturn());
        }

        private void setResult(Object obj) {
            resultObject = obj;
            runSemaphore.release();
        }
    }

    private static String UI_CALL_PACKAGE = null;

    public static Intent buildCallUiIntent(Context ctxt, SipCallSession callInfo) {
        // Resolve the package to handle call.
        if (UI_CALL_PACKAGE == null) {
            UI_CALL_PACKAGE = ctxt.getPackageName();
            try {
                Map<String, DynActivityPlugin> callsUis = ExtraPlugins.getDynActivityPlugins(ctxt,
                        SipManager.ACTION_SIP_CALL_UI);
                String preferredPackage = SipConfigManager.getPreferenceStringValue(ctxt,
                        SipConfigManager.CALL_UI_PACKAGE, UI_CALL_PACKAGE);
                String packageName = null;
                boolean foundPref = false;
                for (String activity : callsUis.keySet()) {
                    packageName = activity.split("/")[0];

                    if (preferredPackage.equalsIgnoreCase(packageName)) {
                        UI_CALL_PACKAGE = packageName;
                        foundPref = true;
                        break;
                    }
                }
                if (!foundPref && !TextUtils.isEmpty(packageName)) {
                    UI_CALL_PACKAGE = packageName;
                }
            } catch (Exception e) {
                Log.e(THIS_FILE, "Error while resolving package", e);
            }
        }
        SipCallSession toSendInfo = new SipCallSession(callInfo);
        Intent intent = new Intent(SipManager.ACTION_SIP_CALL_UI);
        intent.putExtra(SipManager.EXTRA_CALL_INFO, toSendInfo);
        intent.setPackage(UI_CALL_PACKAGE);
        //??
        //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP );
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return intent;
    }

    public static void setVideoWindow(int callId, SurfaceView window, boolean local) {
        if (singleton != null) {
            if (local) {
                singleton.setCaptureVideoWindow(window);
            } else {
                singleton.setRenderVideoWindow(callId, window);
            }
        }
    }

    private void setRenderVideoWindow(final int callId, final SurfaceView window) {
        getExecutor().execute(new SipRunnable() {
            @Override
            protected void doRun() throws SameThreadException {
                pjService.setVideoAndroidRenderer(callId, window);
            }
        });
    }

    private void setCaptureVideoWindow(final SurfaceView window) {
        getExecutor().execute(new SipRunnable() {
            @Override
            protected void doRun() throws SameThreadException {
                pjService.setVideoAndroidCapturer(window);
            }
        });
    }

    private PresenceStatus presence = SipManager.PresenceStatus.ONLINE;

    /**
     * Get current last status for the user
     * @return
     */
    public PresenceStatus getPresence() {
        return presence;
    }

}