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