1 /* 2 * Copyright (C) 2010, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.sip; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.net.ConnectivityManager; 26 import android.net.NetworkInfo; 27 import android.net.sip.ISipService; 28 import android.net.sip.ISipSession; 29 import android.net.sip.ISipSessionListener; 30 import android.net.sip.SipErrorCode; 31 import android.net.sip.SipManager; 32 import android.net.sip.SipProfile; 33 import android.net.sip.SipSession; 34 import android.net.sip.SipSessionAdapter; 35 import android.net.wifi.WifiManager; 36 import android.os.Binder; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.HandlerThread; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.os.PowerManager; 43 import android.os.Process; 44 import android.os.RemoteException; 45 import android.os.ServiceManager; 46 import android.os.SystemClock; 47 import android.text.TextUtils; 48 import android.util.Log; 49 50 import java.io.IOException; 51 import java.net.DatagramSocket; 52 import java.net.InetAddress; 53 import java.net.UnknownHostException; 54 import java.util.ArrayList; 55 import java.util.Collection; 56 import java.util.Comparator; 57 import java.util.HashMap; 58 import java.util.Iterator; 59 import java.util.Map; 60 import java.util.Timer; 61 import java.util.TimerTask; 62 import java.util.TreeSet; 63 import java.util.concurrent.Executor; 64 import javax.sip.SipException; 65 66 /** 67 * @hide 68 */ 69 public final class SipService extends ISipService.Stub { 70 static final String TAG = "SipService"; 71 static final boolean DEBUG = false; 72 private static final int EXPIRY_TIME = 3600; 73 private static final int SHORT_EXPIRY_TIME = 10; 74 private static final int MIN_EXPIRY_TIME = 60; 75 private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds 76 private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds 77 78 private Context mContext; 79 private String mLocalIp; 80 private int mNetworkType = -1; 81 private SipWakeupTimer mTimer; 82 private WifiManager.WifiLock mWifiLock; 83 private boolean mSipOnWifiOnly; 84 85 private IntervalMeasurementProcess mIntervalMeasurementProcess; 86 87 private MyExecutor mExecutor = new MyExecutor(); 88 89 // SipProfile URI --> group 90 private Map<String, SipSessionGroupExt> mSipGroups = 91 new HashMap<String, SipSessionGroupExt>(); 92 93 // session ID --> session 94 private Map<String, ISipSession> mPendingSessions = 95 new HashMap<String, ISipSession>(); 96 97 private ConnectivityReceiver mConnectivityReceiver; 98 private SipWakeLock mMyWakeLock; 99 private int mKeepAliveInterval; 100 private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; 101 102 /** 103 * Starts the SIP service. Do nothing if the SIP API is not supported on the 104 * device. 105 */ 106 public static void start(Context context) { 107 if (SipManager.isApiSupported(context)) { 108 ServiceManager.addService("sip", new SipService(context)); 109 context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP)); 110 if (DEBUG) Log.d(TAG, "SIP service started"); 111 } 112 } 113 114 private SipService(Context context) { 115 if (DEBUG) Log.d(TAG, " service started!"); 116 mContext = context; 117 mConnectivityReceiver = new ConnectivityReceiver(); 118 119 mWifiLock = ((WifiManager) 120 context.getSystemService(Context.WIFI_SERVICE)) 121 .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); 122 mWifiLock.setReferenceCounted(false); 123 mSipOnWifiOnly = SipManager.isSipWifiOnly(context); 124 125 mMyWakeLock = new SipWakeLock((PowerManager) 126 context.getSystemService(Context.POWER_SERVICE)); 127 128 mTimer = new SipWakeupTimer(context, mExecutor); 129 } 130 131 public synchronized SipProfile[] getListOfProfiles() { 132 mContext.enforceCallingOrSelfPermission( 133 android.Manifest.permission.USE_SIP, null); 134 boolean isCallerRadio = isCallerRadio(); 135 ArrayList<SipProfile> profiles = new ArrayList<SipProfile>(); 136 for (SipSessionGroupExt group : mSipGroups.values()) { 137 if (isCallerRadio || isCallerCreator(group)) { 138 profiles.add(group.getLocalProfile()); 139 } 140 } 141 return profiles.toArray(new SipProfile[profiles.size()]); 142 } 143 144 public synchronized void open(SipProfile localProfile) { 145 mContext.enforceCallingOrSelfPermission( 146 android.Manifest.permission.USE_SIP, null); 147 localProfile.setCallingUid(Binder.getCallingUid()); 148 try { 149 createGroup(localProfile); 150 } catch (SipException e) { 151 Log.e(TAG, "openToMakeCalls()", e); 152 // TODO: how to send the exception back 153 } 154 } 155 156 public synchronized void open3(SipProfile localProfile, 157 PendingIntent incomingCallPendingIntent, 158 ISipSessionListener listener) { 159 mContext.enforceCallingOrSelfPermission( 160 android.Manifest.permission.USE_SIP, null); 161 localProfile.setCallingUid(Binder.getCallingUid()); 162 if (incomingCallPendingIntent == null) { 163 Log.w(TAG, "incomingCallPendingIntent cannot be null; " 164 + "the profile is not opened"); 165 return; 166 } 167 if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": " 168 + incomingCallPendingIntent + ": " + listener); 169 try { 170 SipSessionGroupExt group = createGroup(localProfile, 171 incomingCallPendingIntent, listener); 172 if (localProfile.getAutoRegistration()) { 173 group.openToReceiveCalls(); 174 updateWakeLocks(); 175 } 176 } catch (SipException e) { 177 Log.e(TAG, "openToReceiveCalls()", e); 178 // TODO: how to send the exception back 179 } 180 } 181 182 private boolean isCallerCreator(SipSessionGroupExt group) { 183 SipProfile profile = group.getLocalProfile(); 184 return (profile.getCallingUid() == Binder.getCallingUid()); 185 } 186 187 private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) { 188 return (isCallerRadio() || isCallerCreator(group)); 189 } 190 191 private boolean isCallerRadio() { 192 return (Binder.getCallingUid() == Process.PHONE_UID); 193 } 194 195 public synchronized void close(String localProfileUri) { 196 mContext.enforceCallingOrSelfPermission( 197 android.Manifest.permission.USE_SIP, null); 198 SipSessionGroupExt group = mSipGroups.get(localProfileUri); 199 if (group == null) return; 200 if (!isCallerCreatorOrRadio(group)) { 201 Log.w(TAG, "only creator or radio can close this profile"); 202 return; 203 } 204 205 group = mSipGroups.remove(localProfileUri); 206 notifyProfileRemoved(group.getLocalProfile()); 207 group.close(); 208 209 updateWakeLocks(); 210 } 211 212 public synchronized boolean isOpened(String localProfileUri) { 213 mContext.enforceCallingOrSelfPermission( 214 android.Manifest.permission.USE_SIP, null); 215 SipSessionGroupExt group = mSipGroups.get(localProfileUri); 216 if (group == null) return false; 217 if (isCallerCreatorOrRadio(group)) { 218 return true; 219 } else { 220 Log.w(TAG, "only creator or radio can query on the profile"); 221 return false; 222 } 223 } 224 225 public synchronized boolean isRegistered(String localProfileUri) { 226 mContext.enforceCallingOrSelfPermission( 227 android.Manifest.permission.USE_SIP, null); 228 SipSessionGroupExt group = mSipGroups.get(localProfileUri); 229 if (group == null) return false; 230 if (isCallerCreatorOrRadio(group)) { 231 return group.isRegistered(); 232 } else { 233 Log.w(TAG, "only creator or radio can query on the profile"); 234 return false; 235 } 236 } 237 238 public synchronized void setRegistrationListener(String localProfileUri, 239 ISipSessionListener listener) { 240 mContext.enforceCallingOrSelfPermission( 241 android.Manifest.permission.USE_SIP, null); 242 SipSessionGroupExt group = mSipGroups.get(localProfileUri); 243 if (group == null) return; 244 if (isCallerCreator(group)) { 245 group.setListener(listener); 246 } else { 247 Log.w(TAG, "only creator can set listener on the profile"); 248 } 249 } 250 251 public synchronized ISipSession createSession(SipProfile localProfile, 252 ISipSessionListener listener) { 253 mContext.enforceCallingOrSelfPermission( 254 android.Manifest.permission.USE_SIP, null); 255 localProfile.setCallingUid(Binder.getCallingUid()); 256 if (mNetworkType == -1) return null; 257 try { 258 SipSessionGroupExt group = createGroup(localProfile); 259 return group.createSession(listener); 260 } catch (SipException e) { 261 if (DEBUG) Log.d(TAG, "createSession()", e); 262 return null; 263 } 264 } 265 266 public synchronized ISipSession getPendingSession(String callId) { 267 mContext.enforceCallingOrSelfPermission( 268 android.Manifest.permission.USE_SIP, null); 269 if (callId == null) return null; 270 return mPendingSessions.get(callId); 271 } 272 273 private String determineLocalIp() { 274 try { 275 DatagramSocket s = new DatagramSocket(); 276 s.connect(InetAddress.getByName("192.168.1.1"), 80); 277 return s.getLocalAddress().getHostAddress(); 278 } catch (IOException e) { 279 if (DEBUG) Log.d(TAG, "determineLocalIp()", e); 280 // dont do anything; there should be a connectivity change going 281 return null; 282 } 283 } 284 285 private SipSessionGroupExt createGroup(SipProfile localProfile) 286 throws SipException { 287 String key = localProfile.getUriString(); 288 SipSessionGroupExt group = mSipGroups.get(key); 289 if (group == null) { 290 group = new SipSessionGroupExt(localProfile, null, null); 291 mSipGroups.put(key, group); 292 notifyProfileAdded(localProfile); 293 } else if (!isCallerCreator(group)) { 294 throw new SipException("only creator can access the profile"); 295 } 296 return group; 297 } 298 299 private SipSessionGroupExt createGroup(SipProfile localProfile, 300 PendingIntent incomingCallPendingIntent, 301 ISipSessionListener listener) throws SipException { 302 String key = localProfile.getUriString(); 303 SipSessionGroupExt group = mSipGroups.get(key); 304 if (group != null) { 305 if (!isCallerCreator(group)) { 306 throw new SipException("only creator can access the profile"); 307 } 308 group.setIncomingCallPendingIntent(incomingCallPendingIntent); 309 group.setListener(listener); 310 } else { 311 group = new SipSessionGroupExt(localProfile, 312 incomingCallPendingIntent, listener); 313 mSipGroups.put(key, group); 314 notifyProfileAdded(localProfile); 315 } 316 return group; 317 } 318 319 private void notifyProfileAdded(SipProfile localProfile) { 320 if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile); 321 Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE); 322 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString()); 323 mContext.sendBroadcast(intent); 324 if (mSipGroups.size() == 1) { 325 registerReceivers(); 326 } 327 } 328 329 private void notifyProfileRemoved(SipProfile localProfile) { 330 if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile); 331 Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE); 332 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString()); 333 mContext.sendBroadcast(intent); 334 if (mSipGroups.size() == 0) { 335 unregisterReceivers(); 336 } 337 } 338 339 private void stopPortMappingMeasurement() { 340 if (mIntervalMeasurementProcess != null) { 341 mIntervalMeasurementProcess.stop(); 342 mIntervalMeasurementProcess = null; 343 } 344 } 345 346 private void startPortMappingLifetimeMeasurement( 347 SipProfile localProfile) { 348 startPortMappingLifetimeMeasurement(localProfile, 349 DEFAULT_MAX_KEEPALIVE_INTERVAL); 350 } 351 352 private void startPortMappingLifetimeMeasurement( 353 SipProfile localProfile, int maxInterval) { 354 if ((mIntervalMeasurementProcess == null) 355 && (mKeepAliveInterval == -1) 356 && isBehindNAT(mLocalIp)) { 357 Log.d(TAG, "start NAT port mapping timeout measurement on " 358 + localProfile.getUriString()); 359 360 int minInterval = mLastGoodKeepAliveInterval; 361 if (minInterval >= maxInterval) { 362 // If mLastGoodKeepAliveInterval also does not work, reset it 363 // to the default min 364 minInterval = mLastGoodKeepAliveInterval 365 = DEFAULT_KEEPALIVE_INTERVAL; 366 Log.d(TAG, " reset min interval to " + minInterval); 367 } 368 mIntervalMeasurementProcess = new IntervalMeasurementProcess( 369 localProfile, minInterval, maxInterval); 370 mIntervalMeasurementProcess.start(); 371 } 372 } 373 374 private void restartPortMappingLifetimeMeasurement( 375 SipProfile localProfile, int maxInterval) { 376 stopPortMappingMeasurement(); 377 mKeepAliveInterval = -1; 378 startPortMappingLifetimeMeasurement(localProfile, maxInterval); 379 } 380 381 private synchronized void addPendingSession(ISipSession session) { 382 try { 383 cleanUpPendingSessions(); 384 mPendingSessions.put(session.getCallId(), session); 385 if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size()); 386 } catch (RemoteException e) { 387 // should not happen with a local call 388 Log.e(TAG, "addPendingSession()", e); 389 } 390 } 391 392 private void cleanUpPendingSessions() throws RemoteException { 393 Map.Entry<String, ISipSession>[] entries = 394 mPendingSessions.entrySet().toArray( 395 new Map.Entry[mPendingSessions.size()]); 396 for (Map.Entry<String, ISipSession> entry : entries) { 397 if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) { 398 mPendingSessions.remove(entry.getKey()); 399 } 400 } 401 } 402 403 private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup, 404 SipSessionGroup.SipSessionImpl ringingSession) { 405 String callId = ringingSession.getCallId(); 406 for (SipSessionGroupExt group : mSipGroups.values()) { 407 if ((group != ringingGroup) && group.containsSession(callId)) { 408 if (DEBUG) Log.d(TAG, "call self: " 409 + ringingSession.getLocalProfile().getUriString() 410 + " -> " + group.getLocalProfile().getUriString()); 411 return true; 412 } 413 } 414 return false; 415 } 416 417 private synchronized void onKeepAliveIntervalChanged() { 418 for (SipSessionGroupExt group : mSipGroups.values()) { 419 group.onKeepAliveIntervalChanged(); 420 } 421 } 422 423 private int getKeepAliveInterval() { 424 return (mKeepAliveInterval < 0) 425 ? mLastGoodKeepAliveInterval 426 : mKeepAliveInterval; 427 } 428 429 private boolean isBehindNAT(String address) { 430 try { 431 byte[] d = InetAddress.getByName(address).getAddress(); 432 if ((d[0] == 10) || 433 (((0x000000FF & ((int)d[0])) == 172) && 434 ((0x000000F0 & ((int)d[1])) == 16)) || 435 (((0x000000FF & ((int)d[0])) == 192) && 436 ((0x000000FF & ((int)d[1])) == 168))) { 437 return true; 438 } 439 } catch (UnknownHostException e) { 440 Log.e(TAG, "isBehindAT()" + address, e); 441 } 442 return false; 443 } 444 445 private class SipSessionGroupExt extends SipSessionAdapter { 446 private SipSessionGroup mSipGroup; 447 private PendingIntent mIncomingCallPendingIntent; 448 private boolean mOpenedToReceiveCalls; 449 450 private AutoRegistrationProcess mAutoRegistration = 451 new AutoRegistrationProcess(); 452 453 public SipSessionGroupExt(SipProfile localProfile, 454 PendingIntent incomingCallPendingIntent, 455 ISipSessionListener listener) throws SipException { 456 mSipGroup = new SipSessionGroup(duplicate(localProfile), 457 localProfile.getPassword(), mTimer, mMyWakeLock); 458 mIncomingCallPendingIntent = incomingCallPendingIntent; 459 mAutoRegistration.setListener(listener); 460 } 461 462 public SipProfile getLocalProfile() { 463 return mSipGroup.getLocalProfile(); 464 } 465 466 public boolean containsSession(String callId) { 467 return mSipGroup.containsSession(callId); 468 } 469 470 public void onKeepAliveIntervalChanged() { 471 mAutoRegistration.onKeepAliveIntervalChanged(); 472 } 473 474 // TODO: remove this method once SipWakeupTimer can better handle variety 475 // of timeout values 476 void setWakeupTimer(SipWakeupTimer timer) { 477 mSipGroup.setWakeupTimer(timer); 478 } 479 480 private SipProfile duplicate(SipProfile p) { 481 try { 482 return new SipProfile.Builder(p).setPassword("*").build(); 483 } catch (Exception e) { 484 Log.wtf(TAG, "duplicate()", e); 485 throw new RuntimeException("duplicate profile", e); 486 } 487 } 488 489 public void setListener(ISipSessionListener listener) { 490 mAutoRegistration.setListener(listener); 491 } 492 493 public void setIncomingCallPendingIntent(PendingIntent pIntent) { 494 mIncomingCallPendingIntent = pIntent; 495 } 496 497 public void openToReceiveCalls() throws SipException { 498 mOpenedToReceiveCalls = true; 499 if (mNetworkType != -1) { 500 mSipGroup.openToReceiveCalls(this); 501 mAutoRegistration.start(mSipGroup); 502 } 503 if (DEBUG) Log.d(TAG, " openToReceiveCalls: " + getUri() + ": " 504 + mIncomingCallPendingIntent); 505 } 506 507 public void onConnectivityChanged(boolean connected) 508 throws SipException { 509 mSipGroup.onConnectivityChanged(); 510 if (connected) { 511 mSipGroup.reset(); 512 if (mOpenedToReceiveCalls) openToReceiveCalls(); 513 } else { 514 // close mSipGroup but remember mOpenedToReceiveCalls 515 if (DEBUG) Log.d(TAG, " close auto reg temporarily: " 516 + getUri() + ": " + mIncomingCallPendingIntent); 517 mSipGroup.close(); 518 mAutoRegistration.stop(); 519 } 520 } 521 522 public void close() { 523 mOpenedToReceiveCalls = false; 524 mSipGroup.close(); 525 mAutoRegistration.stop(); 526 if (DEBUG) Log.d(TAG, " close: " + getUri() + ": " 527 + mIncomingCallPendingIntent); 528 } 529 530 public ISipSession createSession(ISipSessionListener listener) { 531 return mSipGroup.createSession(listener); 532 } 533 534 @Override 535 public void onRinging(ISipSession s, SipProfile caller, 536 String sessionDescription) { 537 if (DEBUG) Log.d(TAG, "<<<<< onRinging()"); 538 SipSessionGroup.SipSessionImpl session = 539 (SipSessionGroup.SipSessionImpl) s; 540 synchronized (SipService.this) { 541 try { 542 if (!isRegistered() || callingSelf(this, session)) { 543 session.endCall(); 544 return; 545 } 546 547 // send out incoming call broadcast 548 addPendingSession(session); 549 Intent intent = SipManager.createIncomingCallBroadcast( 550 session.getCallId(), sessionDescription); 551 if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": " 552 + caller.getUri() + ": " + session.getCallId() 553 + " " + mIncomingCallPendingIntent); 554 mIncomingCallPendingIntent.send(mContext, 555 SipManager.INCOMING_CALL_RESULT_CODE, intent); 556 } catch (PendingIntent.CanceledException e) { 557 Log.w(TAG, "pendingIntent is canceled, drop incoming call"); 558 session.endCall(); 559 } 560 } 561 } 562 563 @Override 564 public void onError(ISipSession session, int errorCode, 565 String message) { 566 if (DEBUG) Log.d(TAG, "sip session error: " 567 + SipErrorCode.toString(errorCode) + ": " + message); 568 } 569 570 public boolean isOpenedToReceiveCalls() { 571 return mOpenedToReceiveCalls; 572 } 573 574 public boolean isRegistered() { 575 return mAutoRegistration.isRegistered(); 576 } 577 578 private String getUri() { 579 return mSipGroup.getLocalProfileUri(); 580 } 581 } 582 583 private class IntervalMeasurementProcess implements Runnable, 584 SipSessionGroup.KeepAliveProcessCallback { 585 private static final String TAG = "SipKeepAliveInterval"; 586 private static final int MIN_INTERVAL = 5; // in seconds 587 private static final int PASS_THRESHOLD = 10; 588 private static final int MAX_RETRY_COUNT = 5; 589 private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds 590 private SipProfile mLocalProfile; 591 private SipSessionGroupExt mGroup; 592 private SipSessionGroup.SipSessionImpl mSession; 593 private int mMinInterval; 594 private int mMaxInterval; 595 private int mInterval; 596 private int mPassCount; 597 598 public IntervalMeasurementProcess(SipProfile localProfile, 599 int minInterval, int maxInterval) { 600 mMaxInterval = maxInterval; 601 mMinInterval = minInterval; 602 mLocalProfile = localProfile; 603 } 604 605 public void start() { 606 synchronized (SipService.this) { 607 if (mSession != null) { 608 return; 609 } 610 611 mInterval = (mMaxInterval + mMinInterval) / 2; 612 mPassCount = 0; 613 614 // Don't start measurement if the interval is too small 615 if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) { 616 Log.w(TAG, "measurement aborted; interval=[" + 617 mMinInterval + "," + mMaxInterval + "]"); 618 return; 619 } 620 621 try { 622 Log.d(TAG, "start measurement w interval=" + mInterval); 623 624 mGroup = new SipSessionGroupExt(mLocalProfile, null, null); 625 // TODO: remove this line once SipWakeupTimer can better handle 626 // variety of timeout values 627 mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor)); 628 629 mSession = (SipSessionGroup.SipSessionImpl) 630 mGroup.createSession(null); 631 mSession.startKeepAliveProcess(mInterval, this); 632 } catch (Throwable t) { 633 onError(SipErrorCode.CLIENT_ERROR, t.toString()); 634 } 635 } 636 } 637 638 public void stop() { 639 synchronized (SipService.this) { 640 if (mSession != null) { 641 mSession.stopKeepAliveProcess(); 642 mSession = null; 643 } 644 if (mGroup != null) { 645 mGroup.close(); 646 mGroup = null; 647 } 648 mTimer.cancel(this); 649 } 650 } 651 652 private void restart() { 653 synchronized (SipService.this) { 654 // Return immediately if the measurement process is stopped 655 if (mSession == null) return; 656 657 Log.d(TAG, "restart measurement w interval=" + mInterval); 658 try { 659 mSession.stopKeepAliveProcess(); 660 mPassCount = 0; 661 mSession.startKeepAliveProcess(mInterval, this); 662 } catch (SipException e) { 663 Log.e(TAG, "restart()", e); 664 } 665 } 666 } 667 668 private boolean checkTermination() { 669 return ((mMaxInterval - mMinInterval) < MIN_INTERVAL); 670 } 671 672 // SipSessionGroup.KeepAliveProcessCallback 673 @Override 674 public void onResponse(boolean portChanged) { 675 synchronized (SipService.this) { 676 if (!portChanged) { 677 if (++mPassCount != PASS_THRESHOLD) return; 678 // update the interval, since the current interval is good to 679 // keep the port mapping. 680 if (mKeepAliveInterval > 0) { 681 mLastGoodKeepAliveInterval = mKeepAliveInterval; 682 } 683 mKeepAliveInterval = mMinInterval = mInterval; 684 if (DEBUG) { 685 Log.d(TAG, "measured good keepalive interval: " 686 + mKeepAliveInterval); 687 } 688 onKeepAliveIntervalChanged(); 689 } else { 690 // Since the rport is changed, shorten the interval. 691 mMaxInterval = mInterval; 692 } 693 if (checkTermination()) { 694 // update mKeepAliveInterval and stop measurement. 695 stop(); 696 // If all the measurements failed, we still set it to 697 // mMinInterval; If mMinInterval still doesn't work, a new 698 // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL 699 // will be conducted. 700 mKeepAliveInterval = mMinInterval; 701 if (DEBUG) { 702 Log.d(TAG, "measured keepalive interval: " 703 + mKeepAliveInterval); 704 } 705 } else { 706 // calculate the new interval and continue. 707 mInterval = (mMaxInterval + mMinInterval) / 2; 708 if (DEBUG) { 709 Log.d(TAG, "current interval: " + mKeepAliveInterval 710 + ", test new interval: " + mInterval); 711 } 712 restart(); 713 } 714 } 715 } 716 717 // SipSessionGroup.KeepAliveProcessCallback 718 @Override 719 public void onError(int errorCode, String description) { 720 Log.w(TAG, "interval measurement error: " + description); 721 restartLater(); 722 } 723 724 // timeout handler 725 @Override 726 public void run() { 727 mTimer.cancel(this); 728 restart(); 729 } 730 731 private void restartLater() { 732 synchronized (SipService.this) { 733 int interval = NAT_MEASUREMENT_RETRY_INTERVAL; 734 mTimer.cancel(this); 735 mTimer.set(interval * 1000, this); 736 } 737 } 738 } 739 740 private class AutoRegistrationProcess extends SipSessionAdapter 741 implements Runnable, SipSessionGroup.KeepAliveProcessCallback { 742 private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10; 743 private String TAG = "SipAutoReg"; 744 745 private SipSessionGroup.SipSessionImpl mSession; 746 private SipSessionGroup.SipSessionImpl mKeepAliveSession; 747 private SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); 748 private int mBackoff = 1; 749 private boolean mRegistered; 750 private long mExpiryTime; 751 private int mErrorCode; 752 private String mErrorMessage; 753 private boolean mRunning = false; 754 755 private int mKeepAliveSuccessCount = 0; 756 757 private String getAction() { 758 return toString(); 759 } 760 761 public void start(SipSessionGroup group) { 762 if (!mRunning) { 763 mRunning = true; 764 mBackoff = 1; 765 mSession = (SipSessionGroup.SipSessionImpl) 766 group.createSession(this); 767 // return right away if no active network connection. 768 if (mSession == null) return; 769 770 // start unregistration to clear up old registration at server 771 // TODO: when rfc5626 is deployed, use reg-id and sip.instance 772 // in registration to avoid adding duplicate entries to server 773 mMyWakeLock.acquire(mSession); 774 mSession.unregister(); 775 TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString(); 776 } 777 } 778 779 private void startKeepAliveProcess(int interval) { 780 if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval); 781 if (mKeepAliveSession == null) { 782 mKeepAliveSession = mSession.duplicate(); 783 } else { 784 mKeepAliveSession.stopKeepAliveProcess(); 785 } 786 try { 787 mKeepAliveSession.startKeepAliveProcess(interval, this); 788 } catch (SipException e) { 789 Log.e(TAG, "failed to start keepalive w interval=" + interval, 790 e); 791 } 792 } 793 794 private void stopKeepAliveProcess() { 795 if (mKeepAliveSession != null) { 796 mKeepAliveSession.stopKeepAliveProcess(); 797 mKeepAliveSession = null; 798 } 799 mKeepAliveSuccessCount = 0; 800 } 801 802 // SipSessionGroup.KeepAliveProcessCallback 803 @Override 804 public void onResponse(boolean portChanged) { 805 synchronized (SipService.this) { 806 if (portChanged) { 807 int interval = getKeepAliveInterval(); 808 if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) { 809 Log.i(TAG, "keepalive doesn't work with interval " 810 + interval + ", past success count=" 811 + mKeepAliveSuccessCount); 812 if (interval > DEFAULT_KEEPALIVE_INTERVAL) { 813 restartPortMappingLifetimeMeasurement( 814 mSession.getLocalProfile(), interval); 815 mKeepAliveSuccessCount = 0; 816 } 817 } else { 818 if (DEBUG) { 819 Log.i(TAG, "keep keepalive going with interval " 820 + interval + ", past success count=" 821 + mKeepAliveSuccessCount); 822 } 823 mKeepAliveSuccessCount /= 2; 824 } 825 } else { 826 // Start keep-alive interval measurement on the first 827 // successfully kept-alive SipSessionGroup 828 startPortMappingLifetimeMeasurement( 829 mSession.getLocalProfile()); 830 mKeepAliveSuccessCount++; 831 } 832 833 if (!mRunning || !portChanged) return; 834 835 // The keep alive process is stopped when port is changed; 836 // Nullify the session so that the process can be restarted 837 // again when the re-registration is done 838 mKeepAliveSession = null; 839 840 // Acquire wake lock for the registration process. The 841 // lock will be released when registration is complete. 842 mMyWakeLock.acquire(mSession); 843 mSession.register(EXPIRY_TIME); 844 } 845 } 846 847 // SipSessionGroup.KeepAliveProcessCallback 848 @Override 849 public void onError(int errorCode, String description) { 850 if (DEBUG) { 851 Log.e(TAG, "keepalive error: " + description); 852 } 853 onResponse(true); // re-register immediately 854 } 855 856 public void stop() { 857 if (!mRunning) return; 858 mRunning = false; 859 mMyWakeLock.release(mSession); 860 if (mSession != null) { 861 mSession.setListener(null); 862 if (mNetworkType != -1 && mRegistered) mSession.unregister(); 863 } 864 865 mTimer.cancel(this); 866 stopKeepAliveProcess(); 867 868 mRegistered = false; 869 setListener(mProxy.getListener()); 870 } 871 872 public void onKeepAliveIntervalChanged() { 873 if (mKeepAliveSession != null) { 874 int newInterval = getKeepAliveInterval(); 875 if (DEBUG) { 876 Log.v(TAG, "restart keepalive w interval=" + newInterval); 877 } 878 mKeepAliveSuccessCount = 0; 879 startKeepAliveProcess(newInterval); 880 } 881 } 882 883 public void setListener(ISipSessionListener listener) { 884 synchronized (SipService.this) { 885 mProxy.setListener(listener); 886 887 try { 888 int state = (mSession == null) 889 ? SipSession.State.READY_TO_CALL 890 : mSession.getState(); 891 if ((state == SipSession.State.REGISTERING) 892 || (state == SipSession.State.DEREGISTERING)) { 893 mProxy.onRegistering(mSession); 894 } else if (mRegistered) { 895 int duration = (int) 896 (mExpiryTime - SystemClock.elapsedRealtime()); 897 mProxy.onRegistrationDone(mSession, duration); 898 } else if (mErrorCode != SipErrorCode.NO_ERROR) { 899 if (mErrorCode == SipErrorCode.TIME_OUT) { 900 mProxy.onRegistrationTimeout(mSession); 901 } else { 902 mProxy.onRegistrationFailed(mSession, mErrorCode, 903 mErrorMessage); 904 } 905 } else if (mNetworkType == -1) { 906 mProxy.onRegistrationFailed(mSession, 907 SipErrorCode.DATA_CONNECTION_LOST, 908 "no data connection"); 909 } else if (!mRunning) { 910 mProxy.onRegistrationFailed(mSession, 911 SipErrorCode.CLIENT_ERROR, 912 "registration not running"); 913 } else { 914 mProxy.onRegistrationFailed(mSession, 915 SipErrorCode.IN_PROGRESS, 916 String.valueOf(state)); 917 } 918 } catch (Throwable t) { 919 Log.w(TAG, "setListener(): " + t); 920 } 921 } 922 } 923 924 public boolean isRegistered() { 925 return mRegistered; 926 } 927 928 // timeout handler: re-register 929 @Override 930 public void run() { 931 synchronized (SipService.this) { 932 if (!mRunning) return; 933 934 mErrorCode = SipErrorCode.NO_ERROR; 935 mErrorMessage = null; 936 if (DEBUG) Log.d(TAG, "registering"); 937 if (mNetworkType != -1) { 938 mMyWakeLock.acquire(mSession); 939 mSession.register(EXPIRY_TIME); 940 } 941 } 942 } 943 944 private void restart(int duration) { 945 Log.d(TAG, "Refresh registration " + duration + "s later."); 946 mTimer.cancel(this); 947 mTimer.set(duration * 1000, this); 948 } 949 950 private int backoffDuration() { 951 int duration = SHORT_EXPIRY_TIME * mBackoff; 952 if (duration > 3600) { 953 duration = 3600; 954 } else { 955 mBackoff *= 2; 956 } 957 return duration; 958 } 959 960 @Override 961 public void onRegistering(ISipSession session) { 962 if (DEBUG) Log.d(TAG, "onRegistering(): " + session); 963 synchronized (SipService.this) { 964 if (notCurrentSession(session)) return; 965 966 mRegistered = false; 967 mProxy.onRegistering(session); 968 } 969 } 970 971 private boolean notCurrentSession(ISipSession session) { 972 if (session != mSession) { 973 ((SipSessionGroup.SipSessionImpl) session).setListener(null); 974 mMyWakeLock.release(session); 975 return true; 976 } 977 return !mRunning; 978 } 979 980 @Override 981 public void onRegistrationDone(ISipSession session, int duration) { 982 if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session); 983 synchronized (SipService.this) { 984 if (notCurrentSession(session)) return; 985 986 mProxy.onRegistrationDone(session, duration); 987 988 if (duration > 0) { 989 mExpiryTime = SystemClock.elapsedRealtime() 990 + (duration * 1000); 991 992 if (!mRegistered) { 993 mRegistered = true; 994 // allow some overlap to avoid call drop during renew 995 duration -= MIN_EXPIRY_TIME; 996 if (duration < MIN_EXPIRY_TIME) { 997 duration = MIN_EXPIRY_TIME; 998 } 999 restart(duration); 1000 1001 SipProfile localProfile = mSession.getLocalProfile(); 1002 if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp) 1003 || localProfile.getSendKeepAlive())) { 1004 startKeepAliveProcess(getKeepAliveInterval()); 1005 } 1006 } 1007 mMyWakeLock.release(session); 1008 } else { 1009 mRegistered = false; 1010 mExpiryTime = -1L; 1011 if (DEBUG) Log.d(TAG, "Refresh registration immediately"); 1012 run(); 1013 } 1014 } 1015 } 1016 1017 @Override 1018 public void onRegistrationFailed(ISipSession session, int errorCode, 1019 String message) { 1020 if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": " 1021 + SipErrorCode.toString(errorCode) + ": " + message); 1022 synchronized (SipService.this) { 1023 if (notCurrentSession(session)) return; 1024 1025 switch (errorCode) { 1026 case SipErrorCode.INVALID_CREDENTIALS: 1027 case SipErrorCode.SERVER_UNREACHABLE: 1028 if (DEBUG) Log.d(TAG, " pause auto-registration"); 1029 stop(); 1030 break; 1031 default: 1032 restartLater(); 1033 } 1034 1035 mErrorCode = errorCode; 1036 mErrorMessage = message; 1037 mProxy.onRegistrationFailed(session, errorCode, message); 1038 mMyWakeLock.release(session); 1039 } 1040 } 1041 1042 @Override 1043 public void onRegistrationTimeout(ISipSession session) { 1044 if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session); 1045 synchronized (SipService.this) { 1046 if (notCurrentSession(session)) return; 1047 1048 mErrorCode = SipErrorCode.TIME_OUT; 1049 mProxy.onRegistrationTimeout(session); 1050 restartLater(); 1051 mMyWakeLock.release(session); 1052 } 1053 } 1054 1055 private void restartLater() { 1056 mRegistered = false; 1057 restart(backoffDuration()); 1058 } 1059 } 1060 1061 private class ConnectivityReceiver extends BroadcastReceiver { 1062 @Override 1063 public void onReceive(Context context, Intent intent) { 1064 Bundle bundle = intent.getExtras(); 1065 if (bundle != null) { 1066 final NetworkInfo info = (NetworkInfo) 1067 bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO); 1068 1069 // Run the handler in MyExecutor to be protected by wake lock 1070 mExecutor.execute(new Runnable() { 1071 public void run() { 1072 onConnectivityChanged(info); 1073 } 1074 }); 1075 } 1076 } 1077 } 1078 1079 private void registerReceivers() { 1080 mContext.registerReceiver(mConnectivityReceiver, 1081 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); 1082 if (DEBUG) Log.d(TAG, " +++ register receivers"); 1083 } 1084 1085 private void unregisterReceivers() { 1086 mContext.unregisterReceiver(mConnectivityReceiver); 1087 if (DEBUG) Log.d(TAG, " --- unregister receivers"); 1088 1089 // Reset variables maintained by ConnectivityReceiver. 1090 mWifiLock.release(); 1091 mNetworkType = -1; 1092 } 1093 1094 private void updateWakeLocks() { 1095 for (SipSessionGroupExt group : mSipGroups.values()) { 1096 if (group.isOpenedToReceiveCalls()) { 1097 // Also grab the WifiLock when we are disconnected, so the 1098 // system will keep trying to reconnect. It will be released 1099 // when the system eventually connects to something else. 1100 if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) { 1101 mWifiLock.acquire(); 1102 } else { 1103 mWifiLock.release(); 1104 } 1105 return; 1106 } 1107 } 1108 mWifiLock.release(); 1109 mMyWakeLock.reset(); // in case there's a leak 1110 } 1111 1112 private synchronized void onConnectivityChanged(NetworkInfo info) { 1113 // We only care about the default network, and getActiveNetworkInfo() 1114 // is the only way to distinguish them. However, as broadcasts are 1115 // delivered asynchronously, we might miss DISCONNECTED events from 1116 // getActiveNetworkInfo(), which is critical to our SIP stack. To 1117 // solve this, if it is a DISCONNECTED event to our current network, 1118 // respect it. Otherwise get a new one from getActiveNetworkInfo(). 1119 if (info == null || info.isConnected() || info.getType() != mNetworkType) { 1120 ConnectivityManager cm = (ConnectivityManager) 1121 mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 1122 info = cm.getActiveNetworkInfo(); 1123 } 1124 1125 // Some devices limit SIP on Wi-Fi. In this case, if we are not on 1126 // Wi-Fi, treat it as a DISCONNECTED event. 1127 int networkType = (info != null && info.isConnected()) ? info.getType() : -1; 1128 if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) { 1129 networkType = -1; 1130 } 1131 1132 // Ignore the event if the current active network is not changed. 1133 if (mNetworkType == networkType) { 1134 return; 1135 } 1136 if (DEBUG) { 1137 Log.d(TAG, "onConnectivityChanged(): " + mNetworkType + 1138 " -> " + networkType); 1139 } 1140 1141 try { 1142 if (mNetworkType != -1) { 1143 mLocalIp = null; 1144 stopPortMappingMeasurement(); 1145 for (SipSessionGroupExt group : mSipGroups.values()) { 1146 group.onConnectivityChanged(false); 1147 } 1148 } 1149 mNetworkType = networkType; 1150 1151 if (mNetworkType != -1) { 1152 mLocalIp = determineLocalIp(); 1153 mKeepAliveInterval = -1; 1154 mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL; 1155 for (SipSessionGroupExt group : mSipGroups.values()) { 1156 group.onConnectivityChanged(true); 1157 } 1158 } 1159 updateWakeLocks(); 1160 } catch (SipException e) { 1161 Log.e(TAG, "onConnectivityChanged()", e); 1162 } 1163 } 1164 1165 private static Looper createLooper() { 1166 HandlerThread thread = new HandlerThread("SipService.Executor"); 1167 thread.start(); 1168 return thread.getLooper(); 1169 } 1170 1171 // Executes immediate tasks in a single thread. 1172 // Hold/release wake lock for running tasks 1173 private class MyExecutor extends Handler implements Executor { 1174 MyExecutor() { 1175 super(createLooper()); 1176 } 1177 1178 @Override 1179 public void execute(Runnable task) { 1180 mMyWakeLock.acquire(task); 1181 Message.obtain(this, 0/* don't care */, task).sendToTarget(); 1182 } 1183 1184 @Override 1185 public void handleMessage(Message msg) { 1186 if (msg.obj instanceof Runnable) { 1187 executeInternal((Runnable) msg.obj); 1188 } else { 1189 Log.w(TAG, "can't handle msg: " + msg); 1190 } 1191 } 1192 1193 private void executeInternal(Runnable task) { 1194 try { 1195 task.run(); 1196 } catch (Throwable t) { 1197 Log.e(TAG, "run task: " + task, t); 1198 } finally { 1199 mMyWakeLock.release(task); 1200 } 1201 } 1202 } 1203 } 1204