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