Java tutorial
/** * 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; } }