blob: a477fd174dbfec416db1d03ceaee9a5b1b79d518 [file] [log] [blame]
Chung-yih Wang2d942312010-08-05 12:17:37 +08001/*
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
17package com.android.server.sip;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.net.ConnectivityManager;
26import android.net.NetworkInfo;
27import android.net.sip.ISipService;
28import android.net.sip.ISipSession;
29import android.net.sip.ISipSessionListener;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +080030import android.net.sip.SipErrorCode;
Chung-yih Wang2d942312010-08-05 12:17:37 +080031import android.net.sip.SipManager;
32import android.net.sip.SipProfile;
Hung-ying Tyan84a357b2010-09-16 04:11:32 +080033import android.net.sip.SipSession;
Chung-yih Wang2d942312010-08-05 12:17:37 +080034import android.net.sip.SipSessionAdapter;
Chung-yih Wang2d942312010-08-05 12:17:37 +080035import android.net.wifi.WifiManager;
Chung-yih Wang5424c8d2010-08-25 19:02:18 +080036import android.os.Binder;
Chung-yih Wang2d942312010-08-05 12:17:37 +080037import android.os.Bundle;
Hung-ying Tyanb17eae92010-09-19 00:26:38 +080038import android.os.Handler;
39import android.os.HandlerThread;
40import android.os.Looper;
41import android.os.Message;
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +080042import android.os.PowerManager;
Hung-ying Tyan6a534892010-09-13 18:44:33 +080043import android.os.Process;
Chung-yih Wang2d942312010-08-05 12:17:37 +080044import android.os.RemoteException;
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +080045import android.os.ServiceManager;
Chung-yih Wang2d942312010-08-05 12:17:37 +080046import android.os.SystemClock;
47import android.text.TextUtils;
48import android.util.Log;
49
50import java.io.IOException;
51import java.net.DatagramSocket;
52import java.net.InetAddress;
53import java.net.UnknownHostException;
Hung-ying Tyan6a534892010-09-13 18:44:33 +080054import java.util.ArrayList;
Chung-yih Wang2d942312010-08-05 12:17:37 +080055import java.util.Collection;
56import java.util.Comparator;
57import java.util.HashMap;
58import java.util.Iterator;
59import java.util.Map;
60import java.util.Timer;
61import java.util.TimerTask;
62import java.util.TreeSet;
Hung-ying Tyan56215542011-06-14 16:54:18 +080063import java.util.concurrent.Executor;
Chung-yih Wang2d942312010-08-05 12:17:37 +080064import javax.sip.SipException;
65
Chia-chi Yeh95b15c32010-09-02 22:15:26 +080066/**
67 * @hide
68 */
Chung-yih Wang2d942312010-08-05 12:17:37 +080069public final class SipService extends ISipService.Stub {
Hung-ying Tyan28f63c02010-10-15 01:22:44 +080070 static final String TAG = "SipService";
Chia-chi Yehcb6ee062011-11-18 16:57:21 -080071 static final boolean DEBUG = false;
Chung-yih Wang2d942312010-08-05 12:17:37 +080072 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;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +080075 private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
Hung-ying Tyan9edfa102011-07-01 20:32:35 +080076 private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
Chung-yih Wang2d942312010-08-05 12:17:37 +080077
78 private Context mContext;
79 private String mLocalIp;
Chia-chi Yehc2bd6162011-12-27 17:29:35 -080080 private int mNetworkType = -1;
Hung-ying Tyan56215542011-06-14 16:54:18 +080081 private SipWakeupTimer mTimer;
Chung-yih Wang2d942312010-08-05 12:17:37 +080082 private WifiManager.WifiLock mWifiLock;
Chia-chi Yehee59e6a2011-09-27 16:39:38 -070083 private boolean mSipOnWifiOnly;
Hung-ying Tyanf89654d2011-07-01 19:25:46 +080084
Chung-yih Wangbb0a9892011-03-10 11:33:39 +080085 private IntervalMeasurementProcess mIntervalMeasurementProcess;
Chung-yih Wang2d942312010-08-05 12:17:37 +080086
Hung-ying Tyan56215542011-06-14 16:54:18 +080087 private MyExecutor mExecutor = new MyExecutor();
Hung-ying Tyanb17eae92010-09-19 00:26:38 +080088
Chung-yih Wang2d942312010-08-05 12:17:37 +080089 // 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;
Hung-ying Tyan28f63c02010-10-15 01:22:44 +080098 private SipWakeLock mMyWakeLock;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +080099 private int mKeepAliveInterval;
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800100 private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800101
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800102 /**
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800103 * Starts the SIP service. Do nothing if the SIP API is not supported on the
104 * device.
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800105 */
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800106 public static void start(Context context) {
107 if (SipManager.isApiSupported(context)) {
108 ServiceManager.addService("sip", new SipService(context));
Hung-ying Tyan9db99a42010-10-07 09:14:57 +0800109 context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800110 if (DEBUG) Log.d(TAG, "SIP service started");
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800111 }
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800112 }
113
114 private SipService(Context context) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800115 if (DEBUG) Log.d(TAG, " service started!");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800116 mContext = context;
117 mConnectivityReceiver = new ConnectivityReceiver();
Chia-chi Yehee59e6a2011-09-27 16:39:38 -0700118
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
Hung-ying Tyan28f63c02010-10-15 01:22:44 +0800125 mMyWakeLock = new SipWakeLock((PowerManager)
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800126 context.getSystemService(Context.POWER_SERVICE));
Chung-yih Wang2d942312010-08-05 12:17:37 +0800127
Hung-ying Tyan56215542011-06-14 16:54:18 +0800128 mTimer = new SipWakeupTimer(context, mExecutor);
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800129 }
130
Chung-yih Wang2d942312010-08-05 12:17:37 +0800131 public synchronized SipProfile[] getListOfProfiles() {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800132 mContext.enforceCallingOrSelfPermission(
133 android.Manifest.permission.USE_SIP, null);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800134 boolean isCallerRadio = isCallerRadio();
135 ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800136 for (SipSessionGroupExt group : mSipGroups.values()) {
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800137 if (isCallerRadio || isCallerCreator(group)) {
138 profiles.add(group.getLocalProfile());
139 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800140 }
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800141 return profiles.toArray(new SipProfile[profiles.size()]);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800142 }
143
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800144 public synchronized void open(SipProfile localProfile) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800145 mContext.enforceCallingOrSelfPermission(
146 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800147 localProfile.setCallingUid(Binder.getCallingUid());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800148 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
Chung-yih Wang2d942312010-08-05 12:17:37 +0800156 public synchronized void open3(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800157 PendingIntent incomingCallPendingIntent,
158 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800159 mContext.enforceCallingOrSelfPermission(
160 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800161 localProfile.setCallingUid(Binder.getCallingUid());
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800162 if (incomingCallPendingIntent == null) {
163 Log.w(TAG, "incomingCallPendingIntent cannot be null; "
164 + "the profile is not opened");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800165 return;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800166 }
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800167 if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800168 + incomingCallPendingIntent + ": " + listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800169 try {
170 SipSessionGroupExt group = createGroup(localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800171 incomingCallPendingIntent, listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800172 if (localProfile.getAutoRegistration()) {
173 group.openToReceiveCalls();
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800174 updateWakeLocks();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800175 }
176 } catch (SipException e) {
177 Log.e(TAG, "openToReceiveCalls()", e);
178 // TODO: how to send the exception back
179 }
180 }
181
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800182 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
Chung-yih Wang2d942312010-08-05 12:17:37 +0800195 public synchronized void close(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800196 mContext.enforceCallingOrSelfPermission(
197 android.Manifest.permission.USE_SIP, null);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800198 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
199 if (group == null) return;
200 if (!isCallerCreatorOrRadio(group)) {
Joe Onorato431bb222010-10-18 19:13:23 -0400201 Log.w(TAG, "only creator or radio can close this profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800202 return;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800203 }
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800204
205 group = mSipGroups.remove(localProfileUri);
206 notifyProfileRemoved(group.getLocalProfile());
207 group.close();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800208
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800209 updateWakeLocks();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800210 }
211
212 public synchronized boolean isOpened(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800213 mContext.enforceCallingOrSelfPermission(
214 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800215 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800216 if (group == null) return false;
217 if (isCallerCreatorOrRadio(group)) {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800218 return true;
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800219 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400220 Log.w(TAG, "only creator or radio can query on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800221 return false;
222 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800223 }
224
225 public synchronized boolean isRegistered(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800226 mContext.enforceCallingOrSelfPermission(
227 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800228 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800229 if (group == null) return false;
230 if (isCallerCreatorOrRadio(group)) {
231 return group.isRegistered();
232 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400233 Log.w(TAG, "only creator or radio can query on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800234 return false;
235 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800236 }
237
238 public synchronized void setRegistrationListener(String localProfileUri,
239 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800240 mContext.enforceCallingOrSelfPermission(
241 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800242 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800243 if (group == null) return;
244 if (isCallerCreator(group)) {
245 group.setListener(listener);
246 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400247 Log.w(TAG, "only creator can set listener on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800248 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800249 }
250
251 public synchronized ISipSession createSession(SipProfile localProfile,
252 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800253 mContext.enforceCallingOrSelfPermission(
254 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800255 localProfile.setCallingUid(Binder.getCallingUid());
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800256 if (mNetworkType == -1) return null;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800257 try {
258 SipSessionGroupExt group = createGroup(localProfile);
259 return group.createSession(listener);
260 } catch (SipException e) {
Joe Onorato431bb222010-10-18 19:13:23 -0400261 if (DEBUG) Log.d(TAG, "createSession()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800262 return null;
263 }
264 }
265
266 public synchronized ISipSession getPendingSession(String callId) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800267 mContext.enforceCallingOrSelfPermission(
268 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800269 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) {
Joe Onorato431bb222010-10-18 19:13:23 -0400279 if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800280 // 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);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800293 } else if (!isCallerCreator(group)) {
294 throw new SipException("only creator can access the profile");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800295 }
296 return group;
297 }
298
299 private SipSessionGroupExt createGroup(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800300 PendingIntent incomingCallPendingIntent,
301 ISipSessionListener listener) throws SipException {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800302 String key = localProfile.getUriString();
303 SipSessionGroupExt group = mSipGroups.get(key);
304 if (group != null) {
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800305 if (!isCallerCreator(group)) {
306 throw new SipException("only creator can access the profile");
307 }
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800308 group.setIncomingCallPendingIntent(incomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800309 group.setListener(listener);
310 } else {
311 group = new SipSessionGroupExt(localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800312 incomingCallPendingIntent, listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800313 mSipGroups.put(key, group);
314 notifyProfileAdded(localProfile);
315 }
316 return group;
317 }
318
319 private void notifyProfileAdded(SipProfile localProfile) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800320 if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800321 Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
322 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800323 mContext.sendBroadcast(intent);
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800324 if (mSipGroups.size() == 1) {
325 registerReceivers();
326 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800327 }
328
329 private void notifyProfileRemoved(SipProfile localProfile) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800330 if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800331 Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
332 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800333 mContext.sendBroadcast(intent);
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800334 if (mSipGroups.size() == 0) {
335 unregisterReceivers();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800336 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800337 }
338
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800339 private void stopPortMappingMeasurement() {
340 if (mIntervalMeasurementProcess != null) {
341 mIntervalMeasurementProcess.stop();
342 mIntervalMeasurementProcess = null;
343 }
344 }
345
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800346 private void startPortMappingLifetimeMeasurement(
347 SipProfile localProfile) {
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800348 startPortMappingLifetimeMeasurement(localProfile,
349 DEFAULT_MAX_KEEPALIVE_INTERVAL);
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800350 }
351
352 private void startPortMappingLifetimeMeasurement(
353 SipProfile localProfile, int maxInterval) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800354 if ((mIntervalMeasurementProcess == null)
355 && (mKeepAliveInterval == -1)
356 && isBehindNAT(mLocalIp)) {
357 Log.d(TAG, "start NAT port mapping timeout measurement on "
358 + localProfile.getUriString());
359
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800360 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);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800370 mIntervalMeasurementProcess.start();
371 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800372 }
373
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800374 private void restartPortMappingLifetimeMeasurement(
375 SipProfile localProfile, int maxInterval) {
376 stopPortMappingMeasurement();
377 mKeepAliveInterval = -1;
378 startPortMappingLifetimeMeasurement(localProfile, maxInterval);
379 }
380
Chung-yih Wang2d942312010-08-05 12:17:37 +0800381 private synchronized void addPendingSession(ISipSession session) {
382 try {
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800383 cleanUpPendingSessions();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800384 mPendingSessions.put(session.getCallId(), session);
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800385 if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800386 } catch (RemoteException e) {
387 // should not happen with a local call
388 Log.e(TAG, "addPendingSession()", e);
389 }
390 }
391
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800392 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
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800403 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
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800417 private synchronized void onKeepAliveIntervalChanged() {
418 for (SipSessionGroupExt group : mSipGroups.values()) {
419 group.onKeepAliveIntervalChanged();
420 }
421 }
422
423 private int getKeepAliveInterval() {
424 return (mKeepAliveInterval < 0)
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800425 ? mLastGoodKeepAliveInterval
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800426 : 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 }
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800444
Chung-yih Wang2d942312010-08-05 12:17:37 +0800445 private class SipSessionGroupExt extends SipSessionAdapter {
446 private SipSessionGroup mSipGroup;
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800447 private PendingIntent mIncomingCallPendingIntent;
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800448 private boolean mOpenedToReceiveCalls;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800449
450 private AutoRegistrationProcess mAutoRegistration =
451 new AutoRegistrationProcess();
452
453 public SipSessionGroupExt(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800454 PendingIntent incomingCallPendingIntent,
Chung-yih Wang2d942312010-08-05 12:17:37 +0800455 ISipSessionListener listener) throws SipException {
Chia-chi Yeh2dd91342012-04-04 13:04:26 -0700456 mSipGroup = new SipSessionGroup(duplicate(localProfile),
457 localProfile.getPassword(), mTimer, mMyWakeLock);
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800458 mIncomingCallPendingIntent = incomingCallPendingIntent;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800459 mAutoRegistration.setListener(listener);
460 }
461
462 public SipProfile getLocalProfile() {
463 return mSipGroup.getLocalProfile();
464 }
465
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800466 public boolean containsSession(String callId) {
467 return mSipGroup.containsSession(callId);
468 }
469
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800470 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
Chung-yih Wang2d942312010-08-05 12:17:37 +0800480 private SipProfile duplicate(SipProfile p) {
481 try {
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800482 return new SipProfile.Builder(p).setPassword("*").build();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800483 } 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
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800493 public void setIncomingCallPendingIntent(PendingIntent pIntent) {
494 mIncomingCallPendingIntent = pIntent;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800495 }
496
497 public void openToReceiveCalls() throws SipException {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800498 mOpenedToReceiveCalls = true;
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800499 if (mNetworkType != -1) {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800500 mSipGroup.openToReceiveCalls(this);
501 mAutoRegistration.start(mSipGroup);
502 }
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800503 if (DEBUG) Log.d(TAG, " openToReceiveCalls: " + getUri() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800504 + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800505 }
506
507 public void onConnectivityChanged(boolean connected)
508 throws SipException {
Hung-ying Tyand231aa82010-09-14 00:17:51 +0800509 mSipGroup.onConnectivityChanged();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800510 if (connected) {
Chia-chi Yeh2dd91342012-04-04 13:04:26 -0700511 mSipGroup.reset();
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800512 if (mOpenedToReceiveCalls) openToReceiveCalls();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800513 } else {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800514 // close mSipGroup but remember mOpenedToReceiveCalls
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800515 if (DEBUG) Log.d(TAG, " close auto reg temporarily: "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800516 + getUri() + ": " + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800517 mSipGroup.close();
518 mAutoRegistration.stop();
519 }
520 }
521
Hung-ying Tyanfc51f2c2010-09-22 23:51:57 +0800522 public void close() {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800523 mOpenedToReceiveCalls = false;
Hung-ying Tyanfc51f2c2010-09-22 23:51:57 +0800524 mSipGroup.close();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800525 mAutoRegistration.stop();
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800526 if (DEBUG) Log.d(TAG, " close: " + getUri() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800527 + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800528 }
529
530 public ISipSession createSession(ISipSessionListener listener) {
531 return mSipGroup.createSession(listener);
532 }
533
534 @Override
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800535 public void onRinging(ISipSession s, SipProfile caller,
Chia-chi Yeh95b15c32010-09-02 22:15:26 +0800536 String sessionDescription) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800537 if (DEBUG) Log.d(TAG, "<<<<< onRinging()");
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800538 SipSessionGroup.SipSessionImpl session =
539 (SipSessionGroup.SipSessionImpl) s;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800540 synchronized (SipService.this) {
541 try {
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800542 if (!isRegistered() || callingSelf(this, session)) {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800543 session.endCall();
544 return;
545 }
546
547 // send out incoming call broadcast
Chung-yih Wang2d942312010-08-05 12:17:37 +0800548 addPendingSession(session);
549 Intent intent = SipManager.createIncomingCallBroadcast(
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800550 session.getCallId(), sessionDescription);
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800551 if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
552 + caller.getUri() + ": " + session.getCallId()
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800553 + " " + 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();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800559 }
560 }
561 }
562
563 @Override
Hung-ying Tyan97963792010-09-17 16:58:51 +0800564 public void onError(ISipSession session, int errorCode,
Chung-yih Wang2d942312010-08-05 12:17:37 +0800565 String message) {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800566 if (DEBUG) Log.d(TAG, "sip session error: "
567 + SipErrorCode.toString(errorCode) + ": " + message);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800568 }
569
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800570 public boolean isOpenedToReceiveCalls() {
571 return mOpenedToReceiveCalls;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800572 }
573
574 public boolean isRegistered() {
575 return mAutoRegistration.isRegistered();
576 }
577
578 private String getUri() {
579 return mSipGroup.getLocalProfileUri();
580 }
581 }
582
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800583 private class IntervalMeasurementProcess implements Runnable,
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800584 SipSessionGroup.KeepAliveProcessCallback {
585 private static final String TAG = "SipKeepAliveInterval";
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800586 private static final int MIN_INTERVAL = 5; // in seconds
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800587 private static final int PASS_THRESHOLD = 10;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800588 private static final int MAX_RETRY_COUNT = 5;
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800589 private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700590 private SipProfile mLocalProfile;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800591 private SipSessionGroupExt mGroup;
592 private SipSessionGroup.SipSessionImpl mSession;
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800593 private int mMinInterval;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800594 private int mMaxInterval;
595 private int mInterval;
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700596 private int mPassCount;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800597
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800598 public IntervalMeasurementProcess(SipProfile localProfile,
599 int minInterval, int maxInterval) {
600 mMaxInterval = maxInterval;
601 mMinInterval = minInterval;
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700602 mLocalProfile = localProfile;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800603 }
604
605 public void start() {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800606 synchronized (SipService.this) {
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700607 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
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800629 mSession = (SipSessionGroup.SipSessionImpl)
630 mGroup.createSession(null);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800631 mSession.startKeepAliveProcess(mInterval, this);
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700632 } catch (Throwable t) {
633 onError(SipErrorCode.CLIENT_ERROR, t.toString());
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800634 }
635 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800636 }
637
638 public void stop() {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800639 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800640 if (mSession != null) {
641 mSession.stopKeepAliveProcess();
642 mSession = null;
643 }
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700644 if (mGroup != null) {
645 mGroup.close();
646 mGroup = null;
647 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800648 mTimer.cancel(this);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800649 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800650 }
651
652 private void restart() {
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800653 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800654 // Return immediately if the measurement process is stopped
655 if (mSession == null) return;
656
657 Log.d(TAG, "restart measurement w interval=" + mInterval);
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800658 try {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800659 mSession.stopKeepAliveProcess();
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800660 mPassCount = 0;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800661 mSession.startKeepAliveProcess(mInterval, this);
662 } catch (SipException e) {
663 Log.e(TAG, "restart()", e);
664 }
665 }
666 }
667
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800668 private boolean checkTermination() {
669 return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
670 }
671
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800672 // SipSessionGroup.KeepAliveProcessCallback
673 @Override
674 public void onResponse(boolean portChanged) {
675 synchronized (SipService.this) {
676 if (!portChanged) {
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800677 if (++mPassCount != PASS_THRESHOLD) return;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800678 // update the interval, since the current interval is good to
679 // keep the port mapping.
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800680 if (mKeepAliveInterval > 0) {
681 mLastGoodKeepAliveInterval = mKeepAliveInterval;
682 }
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800683 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 }
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800693 if (checkTermination()) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800694 // update mKeepAliveInterval and stop measurement.
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800695 stop();
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800696 // 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.
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800700 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;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800708 if (DEBUG) {
709 Log.d(TAG, "current interval: " + mKeepAliveInterval
710 + ", test new interval: " + mInterval);
711 }
712 restart();
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800713 }
714 }
715 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800716
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800717 // SipSessionGroup.KeepAliveProcessCallback
718 @Override
719 public void onError(int errorCode, String description) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800720 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() {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800732 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800733 int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800734 mTimer.cancel(this);
735 mTimer.set(interval * 1000, this);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800736 }
737 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800738 }
739
740 private class AutoRegistrationProcess extends SipSessionAdapter
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800741 implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800742 private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800743 private String TAG = "SipAutoReg";
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800744
Chung-yih Wang2d942312010-08-05 12:17:37 +0800745 private SipSessionGroup.SipSessionImpl mSession;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800746 private SipSessionGroup.SipSessionImpl mKeepAliveSession;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800747 private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800748 private int mBackoff = 1;
749 private boolean mRegistered;
750 private long mExpiryTime;
Hung-ying Tyan97963792010-09-17 16:58:51 +0800751 private int mErrorCode;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800752 private String mErrorMessage;
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800753 private boolean mRunning = false;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800754
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800755 private int mKeepAliveSuccessCount = 0;
756
Chung-yih Wang2d942312010-08-05 12:17:37 +0800757 private String getAction() {
758 return toString();
759 }
760
761 public void start(SipSessionGroup group) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800762 if (!mRunning) {
763 mRunning = true;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800764 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
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800773 mMyWakeLock.acquire(mSession);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800774 mSession.unregister();
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800775 TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800776 }
777 }
778
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800779 private void startKeepAliveProcess(int interval) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800780 if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval);
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800781 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
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800802 // SipSessionGroup.KeepAliveProcessCallback
803 @Override
804 public void onResponse(boolean portChanged) {
805 synchronized (SipService.this) {
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800806 if (portChanged) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800807 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 {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800818 if (DEBUG) {
819 Log.i(TAG, "keep keepalive going with interval "
820 + interval + ", past success count="
821 + mKeepAliveSuccessCount);
822 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800823 mKeepAliveSuccessCount /= 2;
824 }
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800825 } else {
826 // Start keep-alive interval measurement on the first
827 // successfully kept-alive SipSessionGroup
828 startPortMappingLifetimeMeasurement(
829 mSession.getLocalProfile());
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800830 mKeepAliveSuccessCount++;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800831 }
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800832
833 if (!mRunning || !portChanged) return;
Hung-ying Tyan12750702011-06-23 18:23:09 +0800834
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
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800840 // 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) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800850 if (DEBUG) {
851 Log.e(TAG, "keepalive error: " + description);
852 }
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800853 onResponse(true); // re-register immediately
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800854 }
855
Chung-yih Wang2d942312010-08-05 12:17:37 +0800856 public void stop() {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800857 if (!mRunning) return;
858 mRunning = false;
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800859 mMyWakeLock.release(mSession);
860 if (mSession != null) {
861 mSession.setListener(null);
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800862 if (mNetworkType != -1 && mRegistered) mSession.unregister();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800863 }
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800864
Chung-yih Wang2d942312010-08-05 12:17:37 +0800865 mTimer.cancel(this);
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800866 stopKeepAliveProcess();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800867
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800868 mRegistered = false;
869 setListener(mProxy.getListener());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800870 }
871
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800872 public void onKeepAliveIntervalChanged() {
873 if (mKeepAliveSession != null) {
874 int newInterval = getKeepAliveInterval();
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800875 if (DEBUG) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800876 Log.v(TAG, "restart keepalive w interval=" + newInterval);
877 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800878 mKeepAliveSuccessCount = 0;
879 startKeepAliveProcess(newInterval);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800880 }
881 }
882
Chung-yih Wang2d942312010-08-05 12:17:37 +0800883 public void setListener(ISipSessionListener listener) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800884 synchronized (SipService.this) {
885 mProxy.setListener(listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800886
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800887 try {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800888 int state = (mSession == null)
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800889 ? SipSession.State.READY_TO_CALL
Hung-ying Tyan97963792010-09-17 16:58:51 +0800890 : mSession.getState();
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800891 if ((state == SipSession.State.REGISTERING)
892 || (state == SipSession.State.DEREGISTERING)) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800893 mProxy.onRegistering(mSession);
894 } else if (mRegistered) {
895 int duration = (int)
896 (mExpiryTime - SystemClock.elapsedRealtime());
897 mProxy.onRegistrationDone(mSession, duration);
Hung-ying Tyan97963792010-09-17 16:58:51 +0800898 } else if (mErrorCode != SipErrorCode.NO_ERROR) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800899 if (mErrorCode == SipErrorCode.TIME_OUT) {
900 mProxy.onRegistrationTimeout(mSession);
901 } else {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800902 mProxy.onRegistrationFailed(mSession, mErrorCode,
903 mErrorMessage);
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800904 }
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800905 } else if (mNetworkType == -1) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800906 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));
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800917 }
918 } catch (Throwable t) {
919 Log.w(TAG, "setListener(): " + t);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800920 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800921 }
922 }
923
924 public boolean isRegistered() {
925 return mRegistered;
926 }
927
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800928 // timeout handler: re-register
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800929 @Override
Chung-yih Wang2d942312010-08-05 12:17:37 +0800930 public void run() {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800931 synchronized (SipService.this) {
932 if (!mRunning) return;
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800933
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800934 mErrorCode = SipErrorCode.NO_ERROR;
935 mErrorMessage = null;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800936 if (DEBUG) Log.d(TAG, "registering");
Chia-chi Yehc2bd6162011-12-27 17:29:35 -0800937 if (mNetworkType != -1) {
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800938 mMyWakeLock.acquire(mSession);
939 mSession.register(EXPIRY_TIME);
940 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800941 }
942 }
943
Chung-yih Wang2d942312010-08-05 12:17:37 +0800944 private void restart(int duration) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800945 Log.d(TAG, "Refresh registration " + duration + "s later.");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800946 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) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800962 if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800963 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800964 if (notCurrentSession(session)) return;
965
Chung-yih Wang2d942312010-08-05 12:17:37 +0800966 mRegistered = false;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800967 mProxy.onRegistering(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800968 }
969 }
970
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800971 private boolean notCurrentSession(ISipSession session) {
972 if (session != mSession) {
973 ((SipSessionGroup.SipSessionImpl) session).setListener(null);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800974 mMyWakeLock.release(session);
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800975 return true;
976 }
977 return !mRunning;
978 }
979
Chung-yih Wang2d942312010-08-05 12:17:37 +0800980 @Override
981 public void onRegistrationDone(ISipSession session, int duration) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800982 if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800983 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800984 if (notCurrentSession(session)) return;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800985
986 mProxy.onRegistrationDone(session, duration);
987
Chung-yih Wang2d942312010-08-05 12:17:37 +0800988 if (duration > 0) {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800989 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
Hung-ying Tyan4a267a92011-06-22 16:42:38 +08001001 SipProfile localProfile = mSession.getLocalProfile();
1002 if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
1003 || localProfile.getSendKeepAlive())) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +08001004 startKeepAliveProcess(getKeepAliveInterval());
Chung-yih Wang2d942312010-08-05 12:17:37 +08001005 }
1006 }
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001007 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001008 } else {
1009 mRegistered = false;
1010 mExpiryTime = -1L;
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001011 if (DEBUG) Log.d(TAG, "Refresh registration immediately");
Chung-yih Wang2d942312010-08-05 12:17:37 +08001012 run();
1013 }
1014 }
1015 }
1016
1017 @Override
Hung-ying Tyan97963792010-09-17 16:58:51 +08001018 public void onRegistrationFailed(ISipSession session, int errorCode,
1019 String message) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001020 if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
Hung-ying Tyan97963792010-09-17 16:58:51 +08001021 + SipErrorCode.toString(errorCode) + ": " + message);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001022 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001023 if (notCurrentSession(session)) return;
Chung-yih Wang2d942312010-08-05 12:17:37 +08001024
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001025 switch (errorCode) {
1026 case SipErrorCode.INVALID_CREDENTIALS:
1027 case SipErrorCode.SERVER_UNREACHABLE:
1028 if (DEBUG) Log.d(TAG, " pause auto-registration");
1029 stop();
Hung-ying Tyan685b61b2010-10-12 10:32:29 +08001030 break;
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001031 default:
1032 restartLater();
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001033 }
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001034
1035 mErrorCode = errorCode;
1036 mErrorMessage = message;
1037 mProxy.onRegistrationFailed(session, errorCode, message);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001038 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001039 }
1040 }
1041
1042 @Override
1043 public void onRegistrationTimeout(ISipSession session) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001044 if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001045 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001046 if (notCurrentSession(session)) return;
1047
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001048 mErrorCode = SipErrorCode.TIME_OUT;
1049 mProxy.onRegistrationTimeout(session);
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001050 restartLater();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001051 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001052 }
1053 }
1054
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001055 private void restartLater() {
Chung-yih Wang2d942312010-08-05 12:17:37 +08001056 mRegistered = false;
1057 restart(backoffDuration());
Chung-yih Wang2d942312010-08-05 12:17:37 +08001058 }
1059 }
1060
1061 private class ConnectivityReceiver extends BroadcastReceiver {
Chung-yih Wang2d942312010-08-05 12:17:37 +08001062 @Override
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001063 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);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001068
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001069 // Run the handler in MyExecutor to be protected by wake lock
Hung-ying Tyan56215542011-06-14 16:54:18 +08001070 mExecutor.execute(new Runnable() {
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001071 public void run() {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001072 onConnectivityChanged(info);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001073 }
1074 });
1075 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001076 }
1077 }
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001078
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001079 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();
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001091 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
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001110 }
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().
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001119 if (info == null || info.isConnected() || info.getType() != mNetworkType) {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001120 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.
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001127 int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
1128 if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
1129 networkType = -1;
1130 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001131
1132 // Ignore the event if the current active network is not changed.
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001133 if (mNetworkType == networkType) {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001134 return;
1135 }
1136 if (DEBUG) {
1137 Log.d(TAG, "onConnectivityChanged(): " + mNetworkType +
1138 " -> " + networkType);
1139 }
1140
1141 try {
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001142 if (mNetworkType != -1) {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001143 mLocalIp = null;
1144 stopPortMappingMeasurement();
1145 for (SipSessionGroupExt group : mSipGroups.values()) {
1146 group.onConnectivityChanged(false);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001147 }
1148 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001149 mNetworkType = networkType;
1150
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001151 if (mNetworkType != -1) {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001152 mLocalIp = determineLocalIp();
1153 mKeepAliveInterval = -1;
1154 mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
1155 for (SipSessionGroupExt group : mSipGroups.values()) {
1156 group.onConnectivityChanged(true);
1157 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001158 }
Chia-chi Yehc2bd6162011-12-27 17:29:35 -08001159 updateWakeLocks();
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001160 } catch (SipException e) {
1161 Log.e(TAG, "onConnectivityChanged()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001162 }
1163 }
1164
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001165 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
Hung-ying Tyan56215542011-06-14 16:54:18 +08001173 private class MyExecutor extends Handler implements Executor {
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001174 MyExecutor() {
1175 super(createLooper());
1176 }
1177
Hung-ying Tyan56215542011-06-14 16:54:18 +08001178 @Override
1179 public void execute(Runnable task) {
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001180 mMyWakeLock.acquire(task);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001181 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) {
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001187 executeInternal((Runnable) msg.obj);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001188 } else {
1189 Log.w(TAG, "can't handle msg: " + msg);
1190 }
1191 }
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001192
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 }
Chung-yih Wang2d942312010-08-05 12:17:37 +08001203}