blob: 38a683ed6a0d900da17ffbd973af415f1123abf0 [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;
80 private String mNetworkType;
81 private boolean mConnected;
Hung-ying Tyan56215542011-06-14 16:54:18 +080082 private SipWakeupTimer mTimer;
Chung-yih Wang2d942312010-08-05 12:17:37 +080083 private WifiManager.WifiLock mWifiLock;
Chia-chi Yehee59e6a2011-09-27 16:39:38 -070084 private boolean mSipOnWifiOnly;
Hung-ying Tyanf89654d2011-07-01 19:25:46 +080085
Chung-yih Wangbb0a9892011-03-10 11:33:39 +080086 private IntervalMeasurementProcess mIntervalMeasurementProcess;
Chung-yih Wang2d942312010-08-05 12:17:37 +080087
Hung-ying Tyan56215542011-06-14 16:54:18 +080088 private MyExecutor mExecutor = new MyExecutor();
Hung-ying Tyanb17eae92010-09-19 00:26:38 +080089
Chung-yih Wang2d942312010-08-05 12:17:37 +080090 // SipProfile URI --> group
91 private Map<String, SipSessionGroupExt> mSipGroups =
92 new HashMap<String, SipSessionGroupExt>();
93
94 // session ID --> session
95 private Map<String, ISipSession> mPendingSessions =
96 new HashMap<String, ISipSession>();
97
98 private ConnectivityReceiver mConnectivityReceiver;
Hung-ying Tyan28f63c02010-10-15 01:22:44 +080099 private SipWakeLock mMyWakeLock;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800100 private int mKeepAliveInterval;
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800101 private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800102
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800103 /**
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800104 * Starts the SIP service. Do nothing if the SIP API is not supported on the
105 * device.
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800106 */
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800107 public static void start(Context context) {
108 if (SipManager.isApiSupported(context)) {
109 ServiceManager.addService("sip", new SipService(context));
Hung-ying Tyan9db99a42010-10-07 09:14:57 +0800110 context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800111 if (DEBUG) Log.d(TAG, "SIP service started");
Hung-ying Tyan7e54ef72010-09-25 22:49:59 +0800112 }
Hung-ying Tyan3424c022010-08-27 18:08:19 +0800113 }
114
115 private SipService(Context context) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800116 if (DEBUG) Log.d(TAG, " service started!");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800117 mContext = context;
118 mConnectivityReceiver = new ConnectivityReceiver();
Chia-chi Yehee59e6a2011-09-27 16:39:38 -0700119
120 mWifiLock = ((WifiManager)
121 context.getSystemService(Context.WIFI_SERVICE))
122 .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
123 mWifiLock.setReferenceCounted(false);
124 mSipOnWifiOnly = SipManager.isSipWifiOnly(context);
125
Hung-ying Tyan28f63c02010-10-15 01:22:44 +0800126 mMyWakeLock = new SipWakeLock((PowerManager)
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800127 context.getSystemService(Context.POWER_SERVICE));
Chung-yih Wang2d942312010-08-05 12:17:37 +0800128
Hung-ying Tyan56215542011-06-14 16:54:18 +0800129 mTimer = new SipWakeupTimer(context, mExecutor);
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800130 }
131
Chung-yih Wang2d942312010-08-05 12:17:37 +0800132 public synchronized SipProfile[] getListOfProfiles() {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800133 mContext.enforceCallingOrSelfPermission(
134 android.Manifest.permission.USE_SIP, null);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800135 boolean isCallerRadio = isCallerRadio();
136 ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800137 for (SipSessionGroupExt group : mSipGroups.values()) {
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800138 if (isCallerRadio || isCallerCreator(group)) {
139 profiles.add(group.getLocalProfile());
140 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800141 }
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800142 return profiles.toArray(new SipProfile[profiles.size()]);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800143 }
144
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800145 public synchronized void open(SipProfile localProfile) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800146 mContext.enforceCallingOrSelfPermission(
147 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800148 localProfile.setCallingUid(Binder.getCallingUid());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800149 try {
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800150 boolean addingFirstProfile = mSipGroups.isEmpty();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800151 createGroup(localProfile);
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800152 if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800153 } catch (SipException e) {
154 Log.e(TAG, "openToMakeCalls()", e);
155 // TODO: how to send the exception back
156 }
157 }
158
Chung-yih Wang2d942312010-08-05 12:17:37 +0800159 public synchronized void open3(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800160 PendingIntent incomingCallPendingIntent,
161 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800162 mContext.enforceCallingOrSelfPermission(
163 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800164 localProfile.setCallingUid(Binder.getCallingUid());
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800165 if (incomingCallPendingIntent == null) {
166 Log.w(TAG, "incomingCallPendingIntent cannot be null; "
167 + "the profile is not opened");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800168 return;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800169 }
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800170 if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800171 + incomingCallPendingIntent + ": " + listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800172 try {
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800173 boolean addingFirstProfile = mSipGroups.isEmpty();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800174 SipSessionGroupExt group = createGroup(localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800175 incomingCallPendingIntent, listener);
Hung-ying Tyane9b54072011-01-07 11:57:22 +0800176 if (addingFirstProfile && !mSipGroups.isEmpty()) registerReceivers();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800177 if (localProfile.getAutoRegistration()) {
178 group.openToReceiveCalls();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800179 }
180 } catch (SipException e) {
181 Log.e(TAG, "openToReceiveCalls()", e);
182 // TODO: how to send the exception back
183 }
184 }
185
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800186 private boolean isCallerCreator(SipSessionGroupExt group) {
187 SipProfile profile = group.getLocalProfile();
188 return (profile.getCallingUid() == Binder.getCallingUid());
189 }
190
191 private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
192 return (isCallerRadio() || isCallerCreator(group));
193 }
194
195 private boolean isCallerRadio() {
196 return (Binder.getCallingUid() == Process.PHONE_UID);
197 }
198
Chung-yih Wang2d942312010-08-05 12:17:37 +0800199 public synchronized void close(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800200 mContext.enforceCallingOrSelfPermission(
201 android.Manifest.permission.USE_SIP, null);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800202 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
203 if (group == null) return;
204 if (!isCallerCreatorOrRadio(group)) {
Joe Onorato431bb222010-10-18 19:13:23 -0400205 Log.w(TAG, "only creator or radio can close this profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800206 return;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800207 }
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800208
209 group = mSipGroups.remove(localProfileUri);
210 notifyProfileRemoved(group.getLocalProfile());
211 group.close();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800212
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800213 if (!anyOpenedToReceiveCalls()) {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -0700214 unregisterReceivers();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800215 mMyWakeLock.reset(); // in case there's leak
216 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800217 }
218
219 public synchronized boolean isOpened(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800220 mContext.enforceCallingOrSelfPermission(
221 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800222 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800223 if (group == null) return false;
224 if (isCallerCreatorOrRadio(group)) {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800225 return true;
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800226 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400227 Log.w(TAG, "only creator or radio can query on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800228 return false;
229 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800230 }
231
232 public synchronized boolean isRegistered(String localProfileUri) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800233 mContext.enforceCallingOrSelfPermission(
234 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800235 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800236 if (group == null) return false;
237 if (isCallerCreatorOrRadio(group)) {
238 return group.isRegistered();
239 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400240 Log.w(TAG, "only creator or radio can query on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800241 return false;
242 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800243 }
244
245 public synchronized void setRegistrationListener(String localProfileUri,
246 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800247 mContext.enforceCallingOrSelfPermission(
248 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800249 SipSessionGroupExt group = mSipGroups.get(localProfileUri);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800250 if (group == null) return;
251 if (isCallerCreator(group)) {
252 group.setListener(listener);
253 } else {
Joe Onorato431bb222010-10-18 19:13:23 -0400254 Log.w(TAG, "only creator can set listener on the profile");
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800255 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800256 }
257
258 public synchronized ISipSession createSession(SipProfile localProfile,
259 ISipSessionListener listener) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800260 mContext.enforceCallingOrSelfPermission(
261 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800262 localProfile.setCallingUid(Binder.getCallingUid());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800263 if (!mConnected) return null;
264 try {
265 SipSessionGroupExt group = createGroup(localProfile);
266 return group.createSession(listener);
267 } catch (SipException e) {
Joe Onorato431bb222010-10-18 19:13:23 -0400268 if (DEBUG) Log.d(TAG, "createSession()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800269 return null;
270 }
271 }
272
273 public synchronized ISipSession getPendingSession(String callId) {
Hung-ying Tyanaa562ff2010-10-08 08:59:38 +0800274 mContext.enforceCallingOrSelfPermission(
275 android.Manifest.permission.USE_SIP, null);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800276 if (callId == null) return null;
277 return mPendingSessions.get(callId);
278 }
279
280 private String determineLocalIp() {
281 try {
282 DatagramSocket s = new DatagramSocket();
283 s.connect(InetAddress.getByName("192.168.1.1"), 80);
284 return s.getLocalAddress().getHostAddress();
285 } catch (IOException e) {
Joe Onorato431bb222010-10-18 19:13:23 -0400286 if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800287 // dont do anything; there should be a connectivity change going
288 return null;
289 }
290 }
291
292 private SipSessionGroupExt createGroup(SipProfile localProfile)
293 throws SipException {
294 String key = localProfile.getUriString();
295 SipSessionGroupExt group = mSipGroups.get(key);
296 if (group == null) {
297 group = new SipSessionGroupExt(localProfile, null, null);
298 mSipGroups.put(key, group);
299 notifyProfileAdded(localProfile);
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800300 } else if (!isCallerCreator(group)) {
301 throw new SipException("only creator can access the profile");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800302 }
303 return group;
304 }
305
306 private SipSessionGroupExt createGroup(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800307 PendingIntent incomingCallPendingIntent,
308 ISipSessionListener listener) throws SipException {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800309 String key = localProfile.getUriString();
310 SipSessionGroupExt group = mSipGroups.get(key);
311 if (group != null) {
Hung-ying Tyan6a534892010-09-13 18:44:33 +0800312 if (!isCallerCreator(group)) {
313 throw new SipException("only creator can access the profile");
314 }
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800315 group.setIncomingCallPendingIntent(incomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800316 group.setListener(listener);
317 } else {
318 group = new SipSessionGroupExt(localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800319 incomingCallPendingIntent, listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800320 mSipGroups.put(key, group);
321 notifyProfileAdded(localProfile);
322 }
323 return group;
324 }
325
326 private void notifyProfileAdded(SipProfile localProfile) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800327 if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800328 Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
329 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800330 mContext.sendBroadcast(intent);
331 }
332
333 private void notifyProfileRemoved(SipProfile localProfile) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800334 if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800335 Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
336 intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800337 mContext.sendBroadcast(intent);
338 }
339
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800340 private boolean anyOpenedToReceiveCalls() {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800341 for (SipSessionGroupExt group : mSipGroups.values()) {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800342 if (group.isOpenedToReceiveCalls()) return true;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800343 }
344 return false;
345 }
346
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800347 private void stopPortMappingMeasurement() {
348 if (mIntervalMeasurementProcess != null) {
349 mIntervalMeasurementProcess.stop();
350 mIntervalMeasurementProcess = null;
351 }
352 }
353
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800354 private void startPortMappingLifetimeMeasurement(
355 SipProfile localProfile) {
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800356 startPortMappingLifetimeMeasurement(localProfile,
357 DEFAULT_MAX_KEEPALIVE_INTERVAL);
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800358 }
359
360 private void startPortMappingLifetimeMeasurement(
361 SipProfile localProfile, int maxInterval) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800362 if ((mIntervalMeasurementProcess == null)
363 && (mKeepAliveInterval == -1)
364 && isBehindNAT(mLocalIp)) {
365 Log.d(TAG, "start NAT port mapping timeout measurement on "
366 + localProfile.getUriString());
367
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800368 int minInterval = mLastGoodKeepAliveInterval;
369 if (minInterval >= maxInterval) {
370 // If mLastGoodKeepAliveInterval also does not work, reset it
371 // to the default min
372 minInterval = mLastGoodKeepAliveInterval
373 = DEFAULT_KEEPALIVE_INTERVAL;
374 Log.d(TAG, " reset min interval to " + minInterval);
375 }
376 mIntervalMeasurementProcess = new IntervalMeasurementProcess(
377 localProfile, minInterval, maxInterval);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800378 mIntervalMeasurementProcess.start();
379 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800380 }
381
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800382 private void restartPortMappingLifetimeMeasurement(
383 SipProfile localProfile, int maxInterval) {
384 stopPortMappingMeasurement();
385 mKeepAliveInterval = -1;
386 startPortMappingLifetimeMeasurement(localProfile, maxInterval);
387 }
388
Chung-yih Wang2d942312010-08-05 12:17:37 +0800389 private synchronized void addPendingSession(ISipSession session) {
390 try {
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800391 cleanUpPendingSessions();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800392 mPendingSessions.put(session.getCallId(), session);
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800393 if (DEBUG) Log.d(TAG, "#pending sess=" + mPendingSessions.size());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800394 } catch (RemoteException e) {
395 // should not happen with a local call
396 Log.e(TAG, "addPendingSession()", e);
397 }
398 }
399
Hung-ying Tyan60c45d02010-10-22 09:01:49 +0800400 private void cleanUpPendingSessions() throws RemoteException {
401 Map.Entry<String, ISipSession>[] entries =
402 mPendingSessions.entrySet().toArray(
403 new Map.Entry[mPendingSessions.size()]);
404 for (Map.Entry<String, ISipSession> entry : entries) {
405 if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
406 mPendingSessions.remove(entry.getKey());
407 }
408 }
409 }
410
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800411 private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
412 SipSessionGroup.SipSessionImpl ringingSession) {
413 String callId = ringingSession.getCallId();
414 for (SipSessionGroupExt group : mSipGroups.values()) {
415 if ((group != ringingGroup) && group.containsSession(callId)) {
416 if (DEBUG) Log.d(TAG, "call self: "
417 + ringingSession.getLocalProfile().getUriString()
418 + " -> " + group.getLocalProfile().getUriString());
419 return true;
420 }
421 }
422 return false;
423 }
424
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800425 private synchronized void onKeepAliveIntervalChanged() {
426 for (SipSessionGroupExt group : mSipGroups.values()) {
427 group.onKeepAliveIntervalChanged();
428 }
429 }
430
431 private int getKeepAliveInterval() {
432 return (mKeepAliveInterval < 0)
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800433 ? mLastGoodKeepAliveInterval
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800434 : mKeepAliveInterval;
435 }
436
437 private boolean isBehindNAT(String address) {
438 try {
439 byte[] d = InetAddress.getByName(address).getAddress();
440 if ((d[0] == 10) ||
441 (((0x000000FF & ((int)d[0])) == 172) &&
442 ((0x000000F0 & ((int)d[1])) == 16)) ||
443 (((0x000000FF & ((int)d[0])) == 192) &&
444 ((0x000000FF & ((int)d[1])) == 168))) {
445 return true;
446 }
447 } catch (UnknownHostException e) {
448 Log.e(TAG, "isBehindAT()" + address, e);
449 }
450 return false;
451 }
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800452
Chung-yih Wang2d942312010-08-05 12:17:37 +0800453 private class SipSessionGroupExt extends SipSessionAdapter {
454 private SipSessionGroup mSipGroup;
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800455 private PendingIntent mIncomingCallPendingIntent;
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800456 private boolean mOpenedToReceiveCalls;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800457
458 private AutoRegistrationProcess mAutoRegistration =
459 new AutoRegistrationProcess();
460
461 public SipSessionGroupExt(SipProfile localProfile,
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800462 PendingIntent incomingCallPendingIntent,
Chung-yih Wang2d942312010-08-05 12:17:37 +0800463 ISipSessionListener listener) throws SipException {
464 String password = localProfile.getPassword();
465 SipProfile p = duplicate(localProfile);
466 mSipGroup = createSipSessionGroup(mLocalIp, p, password);
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800467 mIncomingCallPendingIntent = incomingCallPendingIntent;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800468 mAutoRegistration.setListener(listener);
469 }
470
471 public SipProfile getLocalProfile() {
472 return mSipGroup.getLocalProfile();
473 }
474
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800475 public boolean containsSession(String callId) {
476 return mSipGroup.containsSession(callId);
477 }
478
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800479 public void onKeepAliveIntervalChanged() {
480 mAutoRegistration.onKeepAliveIntervalChanged();
481 }
482
483 // TODO: remove this method once SipWakeupTimer can better handle variety
484 // of timeout values
485 void setWakeupTimer(SipWakeupTimer timer) {
486 mSipGroup.setWakeupTimer(timer);
487 }
488
Chung-yih Wang2d942312010-08-05 12:17:37 +0800489 // network connectivity is tricky because network can be disconnected
490 // at any instant so need to deal with exceptions carefully even when
491 // you think you are connected
492 private SipSessionGroup createSipSessionGroup(String localIp,
493 SipProfile localProfile, String password) throws SipException {
494 try {
Hung-ying Tyan28f63c02010-10-15 01:22:44 +0800495 return new SipSessionGroup(localIp, localProfile, password,
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800496 mTimer, mMyWakeLock);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800497 } catch (IOException e) {
498 // network disconnected
499 Log.w(TAG, "createSipSessionGroup(): network disconnected?");
500 if (localIp != null) {
501 return createSipSessionGroup(null, localProfile, password);
502 } else {
503 // recursive
Joe Onorato431bb222010-10-18 19:13:23 -0400504 Log.wtf(TAG, "impossible! recursive!");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800505 throw new RuntimeException("createSipSessionGroup");
506 }
507 }
508 }
509
510 private SipProfile duplicate(SipProfile p) {
511 try {
Chung-yih Wang5424c8d2010-08-25 19:02:18 +0800512 return new SipProfile.Builder(p).setPassword("*").build();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800513 } catch (Exception e) {
514 Log.wtf(TAG, "duplicate()", e);
515 throw new RuntimeException("duplicate profile", e);
516 }
517 }
518
519 public void setListener(ISipSessionListener listener) {
520 mAutoRegistration.setListener(listener);
521 }
522
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800523 public void setIncomingCallPendingIntent(PendingIntent pIntent) {
524 mIncomingCallPendingIntent = pIntent;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800525 }
526
527 public void openToReceiveCalls() throws SipException {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800528 mOpenedToReceiveCalls = true;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800529 if (mConnected) {
530 mSipGroup.openToReceiveCalls(this);
531 mAutoRegistration.start(mSipGroup);
532 }
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800533 if (DEBUG) Log.d(TAG, " openToReceiveCalls: " + getUri() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800534 + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800535 }
536
537 public void onConnectivityChanged(boolean connected)
538 throws SipException {
Hung-ying Tyand231aa82010-09-14 00:17:51 +0800539 mSipGroup.onConnectivityChanged();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800540 if (connected) {
541 resetGroup(mLocalIp);
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800542 if (mOpenedToReceiveCalls) openToReceiveCalls();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800543 } else {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800544 // close mSipGroup but remember mOpenedToReceiveCalls
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800545 if (DEBUG) Log.d(TAG, " close auto reg temporarily: "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800546 + getUri() + ": " + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800547 mSipGroup.close();
548 mAutoRegistration.stop();
549 }
550 }
551
552 private void resetGroup(String localIp) throws SipException {
553 try {
554 mSipGroup.reset(localIp);
555 } catch (IOException e) {
556 // network disconnected
557 Log.w(TAG, "resetGroup(): network disconnected?");
558 if (localIp != null) {
559 resetGroup(null); // reset w/o local IP
560 } else {
561 // recursive
562 Log.wtf(TAG, "impossible!");
563 throw new RuntimeException("resetGroup");
564 }
565 }
566 }
567
Hung-ying Tyanfc51f2c2010-09-22 23:51:57 +0800568 public void close() {
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800569 mOpenedToReceiveCalls = false;
Hung-ying Tyanfc51f2c2010-09-22 23:51:57 +0800570 mSipGroup.close();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800571 mAutoRegistration.stop();
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800572 if (DEBUG) Log.d(TAG, " close: " + getUri() + ": "
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800573 + mIncomingCallPendingIntent);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800574 }
575
576 public ISipSession createSession(ISipSessionListener listener) {
577 return mSipGroup.createSession(listener);
578 }
579
580 @Override
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800581 public void onRinging(ISipSession s, SipProfile caller,
Chia-chi Yeh95b15c32010-09-02 22:15:26 +0800582 String sessionDescription) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800583 if (DEBUG) Log.d(TAG, "<<<<< onRinging()");
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800584 SipSessionGroup.SipSessionImpl session =
585 (SipSessionGroup.SipSessionImpl) s;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800586 synchronized (SipService.this) {
587 try {
Hung-ying Tyan0a6e7172010-10-18 21:04:18 +0800588 if (!isRegistered() || callingSelf(this, session)) {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800589 session.endCall();
590 return;
591 }
592
593 // send out incoming call broadcast
Chung-yih Wang2d942312010-08-05 12:17:37 +0800594 addPendingSession(session);
595 Intent intent = SipManager.createIncomingCallBroadcast(
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800596 session.getCallId(), sessionDescription);
Hung-ying Tyanc7510582010-09-16 05:45:19 +0800597 if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
598 + caller.getUri() + ": " + session.getCallId()
Hung-ying Tyan323d3672010-10-05 09:35:38 +0800599 + " " + mIncomingCallPendingIntent);
600 mIncomingCallPendingIntent.send(mContext,
601 SipManager.INCOMING_CALL_RESULT_CODE, intent);
602 } catch (PendingIntent.CanceledException e) {
603 Log.w(TAG, "pendingIntent is canceled, drop incoming call");
604 session.endCall();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800605 }
606 }
607 }
608
609 @Override
Hung-ying Tyan97963792010-09-17 16:58:51 +0800610 public void onError(ISipSession session, int errorCode,
Chung-yih Wang2d942312010-08-05 12:17:37 +0800611 String message) {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800612 if (DEBUG) Log.d(TAG, "sip session error: "
613 + SipErrorCode.toString(errorCode) + ": " + message);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800614 }
615
Hung-ying Tyan262cdfc2010-11-02 15:15:43 +0800616 public boolean isOpenedToReceiveCalls() {
617 return mOpenedToReceiveCalls;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800618 }
619
620 public boolean isRegistered() {
621 return mAutoRegistration.isRegistered();
622 }
623
624 private String getUri() {
625 return mSipGroup.getLocalProfileUri();
626 }
627 }
628
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800629 private class IntervalMeasurementProcess implements Runnable,
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800630 SipSessionGroup.KeepAliveProcessCallback {
631 private static final String TAG = "SipKeepAliveInterval";
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800632 private static final int MIN_INTERVAL = 5; // in seconds
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800633 private static final int PASS_THRESHOLD = 10;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800634 private static final int MAX_RETRY_COUNT = 5;
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800635 private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700636 private SipProfile mLocalProfile;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800637 private SipSessionGroupExt mGroup;
638 private SipSessionGroup.SipSessionImpl mSession;
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800639 private int mMinInterval;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800640 private int mMaxInterval;
641 private int mInterval;
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700642 private int mPassCount;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800643
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800644 public IntervalMeasurementProcess(SipProfile localProfile,
645 int minInterval, int maxInterval) {
646 mMaxInterval = maxInterval;
647 mMinInterval = minInterval;
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700648 mLocalProfile = localProfile;
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800649 }
650
651 public void start() {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800652 synchronized (SipService.this) {
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700653 if (mSession != null) {
654 return;
655 }
656
657 mInterval = (mMaxInterval + mMinInterval) / 2;
658 mPassCount = 0;
659
660 // Don't start measurement if the interval is too small
661 if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
662 Log.w(TAG, "measurement aborted; interval=[" +
663 mMinInterval + "," + mMaxInterval + "]");
664 return;
665 }
666
667 try {
668 Log.d(TAG, "start measurement w interval=" + mInterval);
669
670 mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
671 // TODO: remove this line once SipWakeupTimer can better handle
672 // variety of timeout values
673 mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
674
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800675 mSession = (SipSessionGroup.SipSessionImpl)
676 mGroup.createSession(null);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800677 mSession.startKeepAliveProcess(mInterval, this);
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700678 } catch (Throwable t) {
679 onError(SipErrorCode.CLIENT_ERROR, t.toString());
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800680 }
681 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800682 }
683
684 public void stop() {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800685 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800686 if (mSession != null) {
687 mSession.stopKeepAliveProcess();
688 mSession = null;
689 }
Chia-chi Yehd17b6d52011-09-08 16:43:50 -0700690 if (mGroup != null) {
691 mGroup.close();
692 mGroup = null;
693 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800694 mTimer.cancel(this);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800695 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800696 }
697
698 private void restart() {
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800699 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800700 // Return immediately if the measurement process is stopped
701 if (mSession == null) return;
702
703 Log.d(TAG, "restart measurement w interval=" + mInterval);
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800704 try {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800705 mSession.stopKeepAliveProcess();
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800706 mPassCount = 0;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800707 mSession.startKeepAliveProcess(mInterval, this);
708 } catch (SipException e) {
709 Log.e(TAG, "restart()", e);
710 }
711 }
712 }
713
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800714 private boolean checkTermination() {
715 return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
716 }
717
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800718 // SipSessionGroup.KeepAliveProcessCallback
719 @Override
720 public void onResponse(boolean portChanged) {
721 synchronized (SipService.this) {
722 if (!portChanged) {
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800723 if (++mPassCount != PASS_THRESHOLD) return;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800724 // update the interval, since the current interval is good to
725 // keep the port mapping.
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800726 if (mKeepAliveInterval > 0) {
727 mLastGoodKeepAliveInterval = mKeepAliveInterval;
728 }
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800729 mKeepAliveInterval = mMinInterval = mInterval;
730 if (DEBUG) {
731 Log.d(TAG, "measured good keepalive interval: "
732 + mKeepAliveInterval);
733 }
734 onKeepAliveIntervalChanged();
735 } else {
736 // Since the rport is changed, shorten the interval.
737 mMaxInterval = mInterval;
738 }
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800739 if (checkTermination()) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800740 // update mKeepAliveInterval and stop measurement.
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800741 stop();
Hung-ying Tyan9edfa102011-07-01 20:32:35 +0800742 // If all the measurements failed, we still set it to
743 // mMinInterval; If mMinInterval still doesn't work, a new
744 // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
745 // will be conducted.
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800746 mKeepAliveInterval = mMinInterval;
747 if (DEBUG) {
748 Log.d(TAG, "measured keepalive interval: "
749 + mKeepAliveInterval);
750 }
751 } else {
752 // calculate the new interval and continue.
753 mInterval = (mMaxInterval + mMinInterval) / 2;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800754 if (DEBUG) {
755 Log.d(TAG, "current interval: " + mKeepAliveInterval
756 + ", test new interval: " + mInterval);
757 }
758 restart();
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800759 }
760 }
761 }
Chung-yih Wangbb0a9892011-03-10 11:33:39 +0800762
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800763 // SipSessionGroup.KeepAliveProcessCallback
764 @Override
765 public void onError(int errorCode, String description) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800766 Log.w(TAG, "interval measurement error: " + description);
767 restartLater();
768 }
769
770 // timeout handler
771 @Override
772 public void run() {
773 mTimer.cancel(this);
774 restart();
775 }
776
777 private void restartLater() {
Chung-yih Wang2d942312010-08-05 12:17:37 +0800778 synchronized (SipService.this) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800779 int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800780 mTimer.cancel(this);
781 mTimer.set(interval * 1000, this);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800782 }
783 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800784 }
785
786 private class AutoRegistrationProcess extends SipSessionAdapter
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800787 implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800788 private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800789 private String TAG = "SipAutoReg";
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800790
Chung-yih Wang2d942312010-08-05 12:17:37 +0800791 private SipSessionGroup.SipSessionImpl mSession;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800792 private SipSessionGroup.SipSessionImpl mKeepAliveSession;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800793 private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800794 private int mBackoff = 1;
795 private boolean mRegistered;
796 private long mExpiryTime;
Hung-ying Tyan97963792010-09-17 16:58:51 +0800797 private int mErrorCode;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800798 private String mErrorMessage;
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800799 private boolean mRunning = false;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800800
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800801 private int mKeepAliveSuccessCount = 0;
802
Chung-yih Wang2d942312010-08-05 12:17:37 +0800803 private String getAction() {
804 return toString();
805 }
806
807 public void start(SipSessionGroup group) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800808 if (!mRunning) {
809 mRunning = true;
Chung-yih Wang2d942312010-08-05 12:17:37 +0800810 mBackoff = 1;
811 mSession = (SipSessionGroup.SipSessionImpl)
812 group.createSession(this);
813 // return right away if no active network connection.
814 if (mSession == null) return;
815
816 // start unregistration to clear up old registration at server
817 // TODO: when rfc5626 is deployed, use reg-id and sip.instance
818 // in registration to avoid adding duplicate entries to server
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800819 mMyWakeLock.acquire(mSession);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800820 mSession.unregister();
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800821 TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800822 }
823 }
824
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800825 private void startKeepAliveProcess(int interval) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800826 if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval);
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800827 if (mKeepAliveSession == null) {
828 mKeepAliveSession = mSession.duplicate();
829 } else {
830 mKeepAliveSession.stopKeepAliveProcess();
831 }
832 try {
833 mKeepAliveSession.startKeepAliveProcess(interval, this);
834 } catch (SipException e) {
835 Log.e(TAG, "failed to start keepalive w interval=" + interval,
836 e);
837 }
838 }
839
840 private void stopKeepAliveProcess() {
841 if (mKeepAliveSession != null) {
842 mKeepAliveSession.stopKeepAliveProcess();
843 mKeepAliveSession = null;
844 }
845 mKeepAliveSuccessCount = 0;
846 }
847
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800848 // SipSessionGroup.KeepAliveProcessCallback
849 @Override
850 public void onResponse(boolean portChanged) {
851 synchronized (SipService.this) {
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800852 if (portChanged) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800853 int interval = getKeepAliveInterval();
854 if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
855 Log.i(TAG, "keepalive doesn't work with interval "
856 + interval + ", past success count="
857 + mKeepAliveSuccessCount);
858 if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
859 restartPortMappingLifetimeMeasurement(
860 mSession.getLocalProfile(), interval);
861 mKeepAliveSuccessCount = 0;
862 }
863 } else {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800864 if (DEBUG) {
865 Log.i(TAG, "keep keepalive going with interval "
866 + interval + ", past success count="
867 + mKeepAliveSuccessCount);
868 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800869 mKeepAliveSuccessCount /= 2;
870 }
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800871 } else {
872 // Start keep-alive interval measurement on the first
873 // successfully kept-alive SipSessionGroup
874 startPortMappingLifetimeMeasurement(
875 mSession.getLocalProfile());
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800876 mKeepAliveSuccessCount++;
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800877 }
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800878
879 if (!mRunning || !portChanged) return;
Hung-ying Tyan12750702011-06-23 18:23:09 +0800880
881 // The keep alive process is stopped when port is changed;
882 // Nullify the session so that the process can be restarted
883 // again when the re-registration is done
884 mKeepAliveSession = null;
885
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800886 // Acquire wake lock for the registration process. The
887 // lock will be released when registration is complete.
888 mMyWakeLock.acquire(mSession);
889 mSession.register(EXPIRY_TIME);
890 }
891 }
892
893 // SipSessionGroup.KeepAliveProcessCallback
894 @Override
895 public void onError(int errorCode, String description) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800896 if (DEBUG) {
897 Log.e(TAG, "keepalive error: " + description);
898 }
Hung-ying Tyane65f3a82011-06-24 15:17:25 +0800899 onResponse(true); // re-register immediately
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800900 }
901
Chung-yih Wang2d942312010-08-05 12:17:37 +0800902 public void stop() {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800903 if (!mRunning) return;
904 mRunning = false;
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800905 mMyWakeLock.release(mSession);
906 if (mSession != null) {
907 mSession.setListener(null);
908 if (mConnected && mRegistered) mSession.unregister();
909 }
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800910
Chung-yih Wang2d942312010-08-05 12:17:37 +0800911 mTimer.cancel(this);
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800912 stopKeepAliveProcess();
Chung-yih Wang2d942312010-08-05 12:17:37 +0800913
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800914 mRegistered = false;
915 setListener(mProxy.getListener());
Chung-yih Wang2d942312010-08-05 12:17:37 +0800916 }
917
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800918 public void onKeepAliveIntervalChanged() {
919 if (mKeepAliveSession != null) {
920 int newInterval = getKeepAliveInterval();
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800921 if (DEBUG) {
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800922 Log.v(TAG, "restart keepalive w interval=" + newInterval);
923 }
Hung-ying Tyan129d0b02011-06-29 18:04:31 +0800924 mKeepAliveSuccessCount = 0;
925 startKeepAliveProcess(newInterval);
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800926 }
927 }
928
Chung-yih Wang2d942312010-08-05 12:17:37 +0800929 public void setListener(ISipSessionListener listener) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800930 synchronized (SipService.this) {
931 mProxy.setListener(listener);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800932
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800933 try {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800934 int state = (mSession == null)
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800935 ? SipSession.State.READY_TO_CALL
Hung-ying Tyan97963792010-09-17 16:58:51 +0800936 : mSession.getState();
Hung-ying Tyan84a357b2010-09-16 04:11:32 +0800937 if ((state == SipSession.State.REGISTERING)
938 || (state == SipSession.State.DEREGISTERING)) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800939 mProxy.onRegistering(mSession);
940 } else if (mRegistered) {
941 int duration = (int)
942 (mExpiryTime - SystemClock.elapsedRealtime());
943 mProxy.onRegistrationDone(mSession, duration);
Hung-ying Tyan97963792010-09-17 16:58:51 +0800944 } else if (mErrorCode != SipErrorCode.NO_ERROR) {
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800945 if (mErrorCode == SipErrorCode.TIME_OUT) {
946 mProxy.onRegistrationTimeout(mSession);
947 } else {
Hung-ying Tyan97963792010-09-17 16:58:51 +0800948 mProxy.onRegistrationFailed(mSession, mErrorCode,
949 mErrorMessage);
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800950 }
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800951 } else if (!mConnected) {
952 mProxy.onRegistrationFailed(mSession,
953 SipErrorCode.DATA_CONNECTION_LOST,
954 "no data connection");
955 } else if (!mRunning) {
956 mProxy.onRegistrationFailed(mSession,
957 SipErrorCode.CLIENT_ERROR,
958 "registration not running");
959 } else {
960 mProxy.onRegistrationFailed(mSession,
961 SipErrorCode.IN_PROGRESS,
962 String.valueOf(state));
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +0800963 }
964 } catch (Throwable t) {
965 Log.w(TAG, "setListener(): " + t);
Chung-yih Wang2d942312010-08-05 12:17:37 +0800966 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800967 }
968 }
969
970 public boolean isRegistered() {
971 return mRegistered;
972 }
973
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800974 // timeout handler: re-register
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800975 @Override
Chung-yih Wang2d942312010-08-05 12:17:37 +0800976 public void run() {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800977 synchronized (SipService.this) {
978 if (!mRunning) return;
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800979
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +0800980 mErrorCode = SipErrorCode.NO_ERROR;
981 mErrorMessage = null;
Hung-ying Tyan4a267a92011-06-22 16:42:38 +0800982 if (DEBUG) Log.d(TAG, "registering");
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +0800983 if (mConnected) {
984 mMyWakeLock.acquire(mSession);
985 mSession.register(EXPIRY_TIME);
986 }
Chung-yih Wang2d942312010-08-05 12:17:37 +0800987 }
988 }
989
Chung-yih Wang2d942312010-08-05 12:17:37 +0800990 private void restart(int duration) {
Chia-chi Yehcb6ee062011-11-18 16:57:21 -0800991 Log.d(TAG, "Refresh registration " + duration + "s later.");
Chung-yih Wang2d942312010-08-05 12:17:37 +0800992 mTimer.cancel(this);
993 mTimer.set(duration * 1000, this);
994 }
995
996 private int backoffDuration() {
997 int duration = SHORT_EXPIRY_TIME * mBackoff;
998 if (duration > 3600) {
999 duration = 3600;
1000 } else {
1001 mBackoff *= 2;
1002 }
1003 return duration;
1004 }
1005
1006 @Override
1007 public void onRegistering(ISipSession session) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001008 if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001009 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001010 if (notCurrentSession(session)) return;
1011
Chung-yih Wang2d942312010-08-05 12:17:37 +08001012 mRegistered = false;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001013 mProxy.onRegistering(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001014 }
1015 }
1016
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001017 private boolean notCurrentSession(ISipSession session) {
1018 if (session != mSession) {
1019 ((SipSessionGroup.SipSessionImpl) session).setListener(null);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001020 mMyWakeLock.release(session);
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001021 return true;
1022 }
1023 return !mRunning;
1024 }
1025
Chung-yih Wang2d942312010-08-05 12:17:37 +08001026 @Override
1027 public void onRegistrationDone(ISipSession session, int duration) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001028 if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001029 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001030 if (notCurrentSession(session)) return;
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001031
1032 mProxy.onRegistrationDone(session, duration);
1033
Chung-yih Wang2d942312010-08-05 12:17:37 +08001034 if (duration > 0) {
Chung-yih Wang2d942312010-08-05 12:17:37 +08001035 mExpiryTime = SystemClock.elapsedRealtime()
1036 + (duration * 1000);
1037
1038 if (!mRegistered) {
1039 mRegistered = true;
1040 // allow some overlap to avoid call drop during renew
1041 duration -= MIN_EXPIRY_TIME;
1042 if (duration < MIN_EXPIRY_TIME) {
1043 duration = MIN_EXPIRY_TIME;
1044 }
1045 restart(duration);
1046
Hung-ying Tyan4a267a92011-06-22 16:42:38 +08001047 SipProfile localProfile = mSession.getLocalProfile();
1048 if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
1049 || localProfile.getSendKeepAlive())) {
Hung-ying Tyan129d0b02011-06-29 18:04:31 +08001050 startKeepAliveProcess(getKeepAliveInterval());
Chung-yih Wang2d942312010-08-05 12:17:37 +08001051 }
1052 }
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001053 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001054 } else {
1055 mRegistered = false;
1056 mExpiryTime = -1L;
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001057 if (DEBUG) Log.d(TAG, "Refresh registration immediately");
Chung-yih Wang2d942312010-08-05 12:17:37 +08001058 run();
1059 }
1060 }
1061 }
1062
1063 @Override
Hung-ying Tyan97963792010-09-17 16:58:51 +08001064 public void onRegistrationFailed(ISipSession session, int errorCode,
1065 String message) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001066 if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
Hung-ying Tyan97963792010-09-17 16:58:51 +08001067 + SipErrorCode.toString(errorCode) + ": " + message);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001068 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001069 if (notCurrentSession(session)) return;
Chung-yih Wang2d942312010-08-05 12:17:37 +08001070
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001071 switch (errorCode) {
1072 case SipErrorCode.INVALID_CREDENTIALS:
1073 case SipErrorCode.SERVER_UNREACHABLE:
1074 if (DEBUG) Log.d(TAG, " pause auto-registration");
1075 stop();
Hung-ying Tyan685b61b2010-10-12 10:32:29 +08001076 break;
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001077 default:
1078 restartLater();
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001079 }
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001080
1081 mErrorCode = errorCode;
1082 mErrorMessage = message;
1083 mProxy.onRegistrationFailed(session, errorCode, message);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001084 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001085 }
1086 }
1087
1088 @Override
1089 public void onRegistrationTimeout(ISipSession session) {
Hung-ying Tyanc7510582010-09-16 05:45:19 +08001090 if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001091 synchronized (SipService.this) {
Hung-ying Tyanfb3a98b2010-09-30 07:49:35 +08001092 if (notCurrentSession(session)) return;
1093
Hung-ying Tyan3d7606a2010-09-12 23:50:38 +08001094 mErrorCode = SipErrorCode.TIME_OUT;
1095 mProxy.onRegistrationTimeout(session);
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001096 restartLater();
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001097 mMyWakeLock.release(session);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001098 }
1099 }
1100
Hung-ying Tyanee8a8842010-10-06 08:33:47 +08001101 private void restartLater() {
Chung-yih Wang2d942312010-08-05 12:17:37 +08001102 mRegistered = false;
1103 restart(backoffDuration());
Chung-yih Wang2d942312010-08-05 12:17:37 +08001104 }
1105 }
1106
1107 private class ConnectivityReceiver extends BroadcastReceiver {
Chung-yih Wang2d942312010-08-05 12:17:37 +08001108 @Override
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001109 public void onReceive(Context context, Intent intent) {
1110 Bundle bundle = intent.getExtras();
1111 if (bundle != null) {
1112 final NetworkInfo info = (NetworkInfo)
1113 bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO);
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001114
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001115 // Run the handler in MyExecutor to be protected by wake lock
Hung-ying Tyan56215542011-06-14 16:54:18 +08001116 mExecutor.execute(new Runnable() {
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001117 public void run() {
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001118 onConnectivityChanged(info);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001119 }
1120 });
1121 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001122 }
1123 }
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001124
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001125 private void registerReceivers() {
1126 mContext.registerReceiver(mConnectivityReceiver,
1127 new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
1128 if (DEBUG) Log.d(TAG, " +++ register receivers");
1129 }
1130
1131 private void unregisterReceivers() {
1132 mContext.unregisterReceiver(mConnectivityReceiver);
1133 if (DEBUG) Log.d(TAG, " --- unregister receivers");
1134
1135 // Reset variables maintained by ConnectivityReceiver.
1136 mWifiLock.release();
1137 mConnected = false;
1138 }
1139
1140 private synchronized void onConnectivityChanged(NetworkInfo info) {
1141 // We only care about the default network, and getActiveNetworkInfo()
1142 // is the only way to distinguish them. However, as broadcasts are
1143 // delivered asynchronously, we might miss DISCONNECTED events from
1144 // getActiveNetworkInfo(), which is critical to our SIP stack. To
1145 // solve this, if it is a DISCONNECTED event to our current network,
1146 // respect it. Otherwise get a new one from getActiveNetworkInfo().
1147 if (info == null || info.isConnected() ||
1148 !info.getTypeName().equals(mNetworkType)) {
1149 ConnectivityManager cm = (ConnectivityManager)
1150 mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1151 info = cm.getActiveNetworkInfo();
1152 }
1153
1154 // Some devices limit SIP on Wi-Fi. In this case, if we are not on
1155 // Wi-Fi, treat it as a DISCONNECTED event.
1156 boolean connected = (info != null && info.isConnected() &&
1157 (!mSipOnWifiOnly || info.getType() == ConnectivityManager.TYPE_WIFI));
1158 String networkType = connected ? info.getTypeName() : "null";
1159
1160 // Ignore the event if the current active network is not changed.
1161 if (connected == mConnected && networkType.equals(mNetworkType)) {
1162 return;
1163 }
1164 if (DEBUG) {
1165 Log.d(TAG, "onConnectivityChanged(): " + mNetworkType +
1166 " -> " + networkType);
1167 }
1168
1169 try {
1170 if (mConnected) {
1171 mLocalIp = null;
1172 stopPortMappingMeasurement();
1173 for (SipSessionGroupExt group : mSipGroups.values()) {
1174 group.onConnectivityChanged(false);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001175 }
1176 }
Chia-chi Yehee59e6a2011-09-27 16:39:38 -07001177
1178 mConnected = connected;
1179 mNetworkType = networkType;
1180
1181 if (connected) {
1182 mLocalIp = determineLocalIp();
1183 mKeepAliveInterval = -1;
1184 mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
1185 for (SipSessionGroupExt group : mSipGroups.values()) {
1186 group.onConnectivityChanged(true);
1187 }
1188
1189 // If we are on Wi-Fi, grab the WifiLock. Otherwise release it.
1190 if (info.getType() == ConnectivityManager.TYPE_WIFI) {
1191 mWifiLock.acquire();
1192 } else {
1193 mWifiLock.release();
1194 }
1195 } else {
1196 // Always grab the WifiLock when we are disconnected, so the
1197 // system will keep trying to reconnect. We will release it
1198 // if we eventually connect via something else.
1199 mWifiLock.acquire();
1200
1201 mMyWakeLock.reset(); // in case there's a leak
1202 }
1203 } catch (SipException e) {
1204 Log.e(TAG, "onConnectivityChanged()", e);
Chung-yih Wang2d942312010-08-05 12:17:37 +08001205 }
1206 }
1207
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001208 private static Looper createLooper() {
1209 HandlerThread thread = new HandlerThread("SipService.Executor");
1210 thread.start();
1211 return thread.getLooper();
1212 }
1213
1214 // Executes immediate tasks in a single thread.
1215 // Hold/release wake lock for running tasks
Hung-ying Tyan56215542011-06-14 16:54:18 +08001216 private class MyExecutor extends Handler implements Executor {
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001217 MyExecutor() {
1218 super(createLooper());
1219 }
1220
Hung-ying Tyan56215542011-06-14 16:54:18 +08001221 @Override
1222 public void execute(Runnable task) {
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001223 mMyWakeLock.acquire(task);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001224 Message.obtain(this, 0/* don't care */, task).sendToTarget();
1225 }
1226
1227 @Override
1228 public void handleMessage(Message msg) {
1229 if (msg.obj instanceof Runnable) {
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001230 executeInternal((Runnable) msg.obj);
Hung-ying Tyanb17eae92010-09-19 00:26:38 +08001231 } else {
1232 Log.w(TAG, "can't handle msg: " + msg);
1233 }
1234 }
Hung-ying Tyanbd57eea2010-10-13 23:32:17 +08001235
1236 private void executeInternal(Runnable task) {
1237 try {
1238 task.run();
1239 } catch (Throwable t) {
1240 Log.e(TAG, "run task: " + task, t);
1241 } finally {
1242 mMyWakeLock.release(task);
1243 }
1244 }
1245 }
Chung-yih Wang2d942312010-08-05 12:17:37 +08001246}