SipService.java revision a072a6e3aa75c9bd038406a5067156463b58551b
19c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/*
29c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Copyright (C) 2010, The Android Open Source Project
39c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
49c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Licensed under the Apache License, Version 2.0 (the "License");
59c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * you may not use this file except in compliance with the License.
69c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * You may obtain a copy of the License at
79c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
89c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *     http://www.apache.org/licenses/LICENSE-2.0
99c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan *
109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * Unless required by applicable law or agreed to in writing, software
119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * distributed under the License is distributed on an "AS IS" BASIS,
129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * See the License for the specific language governing permissions and
149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * limitations under the License.
159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpackage com.android.server.sip;
189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.app.AlarmManager;
209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.app.PendingIntent;
219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.BroadcastReceiver;
229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.Context;
239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.Intent;
249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.content.IntentFilter;
259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.ConnectivityManager;
269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.NetworkInfo;
279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipService;
289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSession;
299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.ISipSessionListener;
309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipErrorCode;
319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipManager;
329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipProfile;
339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSession;
349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.sip.SipSessionAdapter;
359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.net.wifi.WifiManager;
369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Binder;
379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Bundle;
389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Handler;
399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.HandlerThread;
409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Looper;
419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.Message;
42a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyanimport android.os.Process;
439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.RemoteException;
449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.ServiceManager;
459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.os.SystemClock;
469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.text.TextUtils;
479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport android.util.Log;
489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.io.IOException;
509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.DatagramSocket;
519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.InetAddress;
529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.net.UnknownHostException;
53a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyanimport java.util.ArrayList;
549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Collection;
559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Comparator;
569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.HashMap;
579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Iterator;
589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Map;
599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.Timer;
609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.TimerTask;
619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport java.util.TreeSet;
629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanimport javax.sip.SipException;
639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan/**
659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan * @hide
669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan */
679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyanpublic final class SipService extends ISipService.Stub {
689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final String TAG = "SipService";
699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final boolean DEBUG = true;
709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final boolean DEBUG_TIMER = DEBUG && false;
719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int EXPIRY_TIME = 3600;
729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int SHORT_EXPIRY_TIME = 10;
739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static final int MIN_EXPIRY_TIME = 60;
749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Context mContext;
769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mLocalIp;
779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String mNetworkType;
789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mConnected;
799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WakeupTimer mTimer;
809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private WifiManager.WifiLock mWifiLock;
819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean mWifiOnly;
829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private MyExecutor mExecutor;
849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // SipProfile URI --> group
869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, SipSessionGroupExt> mSipGroups =
879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, SipSessionGroupExt>();
889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // session ID --> session
909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private Map<String, ISipSession> mPendingSessions =
919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            new HashMap<String, ISipSession>();
929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private ConnectivityReceiver mConnectivityReceiver;
949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Starts the SIP service. Do nothing if the SIP API is not supported on the
979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * device.
989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public static void start(Context context) {
1009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (SipManager.isApiSupported(context)) {
1019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ServiceManager.addService("sip", new SipService(context));
1029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.i(TAG, "SIP service started");
1039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipService(Context context) {
1079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, " service started!");
1089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext = context;
1099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mConnectivityReceiver = new ConnectivityReceiver();
1109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        context.registerReceiver(mConnectivityReceiver,
1119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
1129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mTimer = new WakeupTimer(context);
1149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mWifiOnly = SipManager.isSipWifiOnly(context);
1159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private MyExecutor getExecutor() {
1189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // create mExecutor lazily
1199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mExecutor == null) mExecutor = new MyExecutor();
1209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mExecutor;
1219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized SipProfile[] getListOfProfiles() {
124a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        boolean isCallerRadio = isCallerRadio();
125a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
1269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
127a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (isCallerRadio || isCallerCreator(group)) {
128a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                profiles.add(group.getLocalProfile());
129a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
1309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
131a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return profiles.toArray(new SipProfile[profiles.size()]);
1329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public void open(SipProfile localProfile) {
1359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
136a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (localProfile.getAutoRegistration() && isCallerRadio()) {
1379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            openToReceiveCalls(localProfile);
1389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
1399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            openToMakeCalls(localProfile);
1409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void openToMakeCalls(SipProfile localProfile) {
1449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
1459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            createGroup(localProfile);
1469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
1479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToMakeCalls()", e);
1489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
1499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void openToReceiveCalls(SipProfile localProfile) {
1539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
1549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
1569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void open3(SipProfile localProfile,
1579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String incomingCallBroadcastAction, ISipSessionListener listener) {
1589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
1599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
160a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.w(TAG, "empty broadcast action for incoming call");
161a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
162a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
163a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (incomingCallBroadcastAction.equals(
164a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                SipManager.ACTION_SIP_INCOMING_CALL) && !isCallerRadio()) {
165a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.w(TAG, "failed to open the profile; "
166a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                    + "the action string is reserved");
167a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
1689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
1709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + incomingCallBroadcastAction + ": " + listener);
1719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
1729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile,
1739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    incomingCallBroadcastAction, listener);
1749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (localProfile.getAutoRegistration()) {
1759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                group.openToReceiveCalls();
1769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (isWifiOn()) grabWifiLock();
1779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
1789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
1799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "openToReceiveCalls()", e);
1809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // TODO: how to send the exception back
1819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
1829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
1839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
184a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreator(SipSessionGroupExt group) {
185a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipProfile profile = group.getLocalProfile();
186a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (profile.getCallingUid() == Binder.getCallingUid());
187a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
188a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
189a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
190a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (isCallerRadio() || isCallerCreator(group));
191a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
192a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
193a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    private boolean isCallerRadio() {
194a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        return (Binder.getCallingUid() == Process.PHONE_UID);
195a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan    }
196a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
1979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void close(String localProfileUri) {
198a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
199a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
200a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (!isCallerCreatorOrRadio(group)) {
201a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.d(TAG, "only creator or radio can close this profile");
202a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return;
2039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
204a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan
205a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group = mSipGroups.remove(localProfileUri);
206a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        notifyProfileRemoved(group.getLocalProfile());
207a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        group.close();
208a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isWifiOn() && !anyOpened()) releaseWifiLock();
2099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isOpened(String localProfileUri) {
2129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
213a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
214a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
215a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return group.isOpened();
216a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
217a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.i(TAG, "only creator or radio can query on the profile");
218a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
219a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized boolean isRegistered(String localProfileUri) {
2239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
224a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return false;
225a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreatorOrRadio(group)) {
226a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return group.isRegistered();
227a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
228a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.i(TAG, "only creator or radio can query on the profile");
229a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            return false;
230a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized void setRegistrationListener(String localProfileUri,
2349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
236a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (group == null) return;
237a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        if (isCallerCreator(group)) {
238a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            group.setListener(listener);
239a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else {
240a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            Log.i(TAG, "only creator can set listener on the profile");
241a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        }
2429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession createSession(SipProfile localProfile,
2459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ISipSessionListener listener) {
2469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        localProfile.setCallingUid(Binder.getCallingUid());
2479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!mConnected) return null;
2489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
2499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipSessionGroupExt group = createGroup(localProfile);
2509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return group.createSession(listener);
2519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
2529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.w(TAG, "createSession()", e);
2539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
2549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    public synchronized ISipSession getPendingSession(String callId) {
2589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (callId == null) return null;
2599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return mPendingSessions.get(callId);
2609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private String determineLocalIp() {
2639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
2649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            DatagramSocket s = new DatagramSocket();
2659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s.connect(InetAddress.getByName("192.168.1.1"), 80);
2669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s.getLocalAddress().getHostAddress();
2679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (IOException e) {
2689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.w(TAG, "determineLocalIp()", e);
2699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // dont do anything; there should be a connectivity change going
2709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return null;
2719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile)
2759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
2769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
2779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
2789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group == null) {
2799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile, null, null);
2809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
2819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
282a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan        } else if (!isCallerCreator(group)) {
283a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            throw new SipException("only creator can access the profile");
2849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
2859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
2869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
2879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
2889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private SipSessionGroupExt createGroup(SipProfile localProfile,
2899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String incomingCallBroadcastAction, ISipSessionListener listener)
2909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            throws SipException {
2919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        String key = localProfile.getUriString();
2929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        SipSessionGroupExt group = mSipGroups.get(key);
2939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (group != null) {
294a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            if (!isCallerCreator(group)) {
295a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan                throw new SipException("only creator can access the profile");
296a072a6e3aa75c9bd038406a5067156463b58551bHung-ying Tyan            }
2979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group.setIncomingCallBroadcastAction(
2989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    incomingCallBroadcastAction);
2999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group.setListener(listener);
3009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else {
3019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            group = new SipSessionGroupExt(localProfile,
3029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    incomingCallBroadcastAction, listener);
3039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroups.put(key, group);
3049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            notifyProfileAdded(localProfile);
3059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return group;
3079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileAdded(SipProfile localProfile) {
3109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
3119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
3129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void notifyProfileRemoved(SipProfile localProfile) {
3179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
3189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
3199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
3209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        mContext.sendBroadcast(intent);
3219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean anyOpened() {
3249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        for (SipSessionGroupExt group : mSipGroups.values()) {
3259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (group.isOpened()) return true;
3269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return false;
3289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void grabWifiLock() {
3319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock == null) {
3329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "acquire wifi lock");
3339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = ((WifiManager)
3349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.WIFI_SERVICE))
3359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
3369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.acquire();
3379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private void releaseWifiLock() {
3419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (mWifiLock != null) {
3429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "release wifi lock");
3439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock.release();
3449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mWifiLock = null;
3459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private boolean isWifiOn() {
3499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        return "WIFI".equalsIgnoreCase(mNetworkType);
3509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        //return (mConnected && "WIFI".equalsIgnoreCase(mNetworkType));
3519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void onConnectivityChanged(
3549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String type, boolean connected) {
3559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (DEBUG) Log.d(TAG, "onConnectivityChanged(): "
3569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + mNetworkType + (mConnected? " CONNECTED" : " DISCONNECTED")
3579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                + " --> " + type + (connected? " CONNECTED" : " DISCONNECTED"));
3589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean sameType = type.equals(mNetworkType);
3609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (!sameType && !connected) return;
3619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wasWifi = "WIFI".equalsIgnoreCase(mNetworkType);
3639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean isWifi = "WIFI".equalsIgnoreCase(type);
3649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOff = (isWifi && !connected) || (wasWifi && !sameType);
3659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        boolean wifiOn = isWifi && connected;
3669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        if (wifiOff) {
3679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            releaseWifiLock();
3689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } else if (wifiOn) {
3699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (anyOpened()) grabWifiLock();
3709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            boolean wasConnected = mConnected;
3749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mNetworkType = type;
3759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mConnected = connected;
3769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (wasConnected) {
3789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = null;
3799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
3809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(false);
3819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
3829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
3859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mLocalIp = determineLocalIp();
3869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                for (SipSessionGroupExt group : mSipGroups.values()) {
3879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    group.onConnectivityChanged(true);
3889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
3899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
3909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (SipException e) {
3929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "onConnectivityChanged()", e);
3939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
3949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
3959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
3969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private synchronized void addPendingSession(ISipSession session) {
3979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        try {
3989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingSessions.put(session.getCallId(), session);
3999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        } catch (RemoteException e) {
4009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // should not happen with a local call
4019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Log.e(TAG, "addPendingSession()", e);
4029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
4049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class SipSessionGroupExt extends SipSessionAdapter {
4069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup mSipGroup;
4079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mIncomingCallBroadcastAction;
4089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean mOpened;
4099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AutoRegistrationProcess mAutoRegistration =
4119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new AutoRegistrationProcess();
4129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipSessionGroupExt(SipProfile localProfile,
4149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String incomingCallBroadcastAction,
4159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ISipSessionListener listener) throws SipException {
4169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String password = localProfile.getPassword();
4179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            SipProfile p = duplicate(localProfile);
4189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup = createSipSessionGroup(mLocalIp, p, password);
4199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mIncomingCallBroadcastAction = incomingCallBroadcastAction;
4209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
4219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public SipProfile getLocalProfile() {
4249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfile();
4259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // network connectivity is tricky because network can be disconnected
4289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // at any instant so need to deal with exceptions carefully even when
4299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // you think you are connected
4309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup createSipSessionGroup(String localIp,
4319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                SipProfile localProfile, String password) throws SipException {
4329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
4339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return new SipSessionGroup(localIp, localProfile, password);
4349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
4359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
4369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "createSipSessionGroup(): network disconnected?");
4379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
4389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return createSipSessionGroup(null, localProfile, password);
4399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
4409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
4419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.wtf(TAG, "impossible!");
4429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("createSipSessionGroup");
4439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
4449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipProfile duplicate(SipProfile p) {
4489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
4499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return new SipProfile.Builder(p).setPassword("*").build();
4509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (Exception e) {
4519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.wtf(TAG, "duplicate()", e);
4529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("duplicate profile", e);
4539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
4579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.setListener(listener);
4589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setIncomingCallBroadcastAction(String action) {
4619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mIncomingCallBroadcastAction = action;
4629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void openToReceiveCalls() throws SipException {
4659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mOpened = true;
4669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mConnected) {
4679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.openToReceiveCalls(this);
4689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.start(mSipGroup);
4699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "  openToReceiveCalls: " + getUri() + ": "
4719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + mIncomingCallBroadcastAction);
4729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onConnectivityChanged(boolean connected)
4759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throws SipException {
4769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.onConnectivityChanged();
4779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (connected) {
4789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                resetGroup(mLocalIp);
4799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mOpened) openToReceiveCalls();
4809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
4819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // close mSipGroup but remember mOpened
4829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "  close auto reg temporarily: "
4839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + getUri() + ": " + mIncomingCallBroadcastAction);
4849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.close();
4859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAutoRegistration.stop();
4869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
4879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
4889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
4899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void resetGroup(String localIp) throws SipException {
4909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
4919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSipGroup.reset(localIp);
4929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (IOException e) {
4939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // network disconnected
4949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "resetGroup(): network disconnected?");
4959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (localIp != null) {
4969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    resetGroup(null); // reset w/o local IP
4979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
4989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // recursive
4999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.wtf(TAG, "impossible!");
5009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    throw new RuntimeException("resetGroup");
5019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void close() {
5069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mOpened = false;
5079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSipGroup.close();
5089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAutoRegistration.stop();
5099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "   close: " + getUri() + ": "
5109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + mIncomingCallBroadcastAction);
5119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public ISipSession createSession(ISipSessionListener listener) {
5149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.createSession(listener);
5159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
5189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRinging(ISipSession session, SipProfile caller,
5199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String sessionDescription) {
5209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
5219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
5229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (!isRegistered()) {
5239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        session.endCall();
5249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
5259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
5269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // send out incoming call broadcast
5289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    addPendingSession(session);
5299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Intent intent = SipManager.createIncomingCallBroadcast(
5309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            session.getCallId(), sessionDescription)
5319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            .setAction(mIncomingCallBroadcastAction);
5329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
5339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + caller.getUri() + ": " + session.getCallId()
5349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + " " + mIncomingCallBroadcastAction);
5359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.sendBroadcast(intent);
5369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (RemoteException e) {
5379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // should never happen with a local call
5389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.e(TAG, "processCall()", e);
5399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
5419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
5449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onError(ISipSession session, int errorCode,
5459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
5469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "sip session error: "
5479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
5489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isOpened() {
5519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mOpened;
5529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
5559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mAutoRegistration.isRegistered();
5569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getUri() {
5599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mSipGroup.getLocalProfileUri();
5609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
5629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
563d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan    // KeepAliveProcess is controlled by AutoRegistrationProcess.
564d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan    // All methods will be invoked in sync with SipService.this except realRun()
5659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class KeepAliveProcess implements Runnable {
5669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TAG = "\\KEEPALIVE/";
5679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final int INTERVAL = 10;
5689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
569d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
5709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
5729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mSession = session;
5739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
5759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start() {
576d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (mRunning) return;
577d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = true;
5789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(INTERVAL * 1000, this);
5799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
581d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        // timeout handler
5829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
583d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) return;
584d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            final SipSessionGroup.SipSessionImpl session = mSession;
585d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
5869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            // delegate to mExecutor
5879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            getExecutor().addTask(new Runnable() {
5889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                public void run() {
589d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    realRun(session);
5909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
5919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            });
5929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
5939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
594d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        // real timeout handler
595d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private void realRun(SipSessionGroup.SipSessionImpl session) {
5969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
597d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
598d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
599d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                session = session.duplicate();
6009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "~~~ keepalive");
6019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mTimer.cancel(this);
6029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                session.sendKeepAlive();
6039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (session.isReRegisterRequired()) {
6049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSession.register(EXPIRY_TIME);
6059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
6069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTimer.set(INTERVAL * 1000, this);
6079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
6089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
612d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
613d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mSession = null;
6149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
6159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
616d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
617d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
618d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            return (session != mSession) || !mRunning;
619d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        }
6209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
6219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class AutoRegistrationProcess extends SipSessionAdapter
6239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            implements Runnable {
6249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionGroup.SipSessionImpl mSession;
6259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
6269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private KeepAliveProcess mKeepAliveProcess;
6279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mBackoff = 1;
6289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean mRegistered;
6299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private long mExpiryTime;
6309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int mErrorCode;
6319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String mErrorMessage;
632d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean mRunning = false;
6339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
6359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
6369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void start(SipSessionGroup group) {
639d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) {
640d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mRunning = true;
6419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff = 1;
6429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession = (SipSessionGroup.SipSessionImpl)
6439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        group.createSession(this);
6449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // return right away if no active network connection.
6459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mSession == null) return;
6469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // start unregistration to clear up old registration at server
6489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // TODO: when rfc5626 is deployed, use reg-id and sip.instance
6499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // in registration to avoid adding duplicate entries to server
6509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mSession.unregister();
6519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess for "
6529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + mSession.getLocalProfile().getUriString());
6539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void stop() {
657d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (!mRunning) return;
658d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRunning = false;
659d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mSession.setListener(null);
6609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mConnected && mRegistered) mSession.unregister();
661d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
6629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
6639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mKeepAliveProcess != null) {
6649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess.stop();
6659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess = null;
6669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
6679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
668d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            mRegistered = false;
669d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            setListener(mProxy.getListener());
6709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
6719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void setListener(ISipSessionListener listener) {
6739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
6749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.setListener(listener);
6759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
6769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                try {
6779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    int state = (mSession == null)
6789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ? SipSession.State.READY_TO_CALL
6799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            : mSession.getState();
6809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == SipSession.State.REGISTERING)
6819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            || (state == SipSession.State.DEREGISTERING)) {
6829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistering(mSession);
6839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mRegistered) {
6849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        int duration = (int)
6859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                (mExpiryTime - SystemClock.elapsedRealtime());
6869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mProxy.onRegistrationDone(mSession, duration);
6879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (mErrorCode != SipErrorCode.NO_ERROR) {
6889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (mErrorCode == SipErrorCode.TIME_OUT) {
6899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationTimeout(mSession);
6909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
6919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mProxy.onRegistrationFailed(mSession, mErrorCode,
6929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    mErrorMessage);
6939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
694d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mConnected) {
695d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
696d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.DATA_CONNECTION_LOST,
697d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "no data connection");
698d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else if (!mRunning) {
699d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
700d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.CLIENT_ERROR,
701d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                "registration not running");
702d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    } else {
703d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        mProxy.onRegistrationFailed(mSession,
704d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                SipErrorCode.IN_PROGRESS,
705d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                                String.valueOf(state));
7069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
7079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } catch (Throwable t) {
7089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    Log.w(TAG, "setListener(): " + t);
7099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
7109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean isRegistered() {
7149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return mRegistered;
7159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
717d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        // timeout handler
7189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void run() {
719d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            synchronized (SipService.this) {
720d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (!mRunning) return;
721d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                final SipSessionGroup.SipSessionImpl session = mSession;
722d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
723d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                // delegate to mExecutor
724d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                getExecutor().addTask(new Runnable() {
725d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    public void run() {
726d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                        realRun(session);
727d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    }
728d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                });
729d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            }
7309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
732d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        // real timeout handler
733d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private void realRun(SipSessionGroup.SipSessionImpl session) {
7349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
735d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
736d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = SipErrorCode.NO_ERROR;
737d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = null;
738d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (DEBUG) Log.d(TAG, "~~~ registering");
739d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (mConnected) session.register(EXPIRY_TIME);
7409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private boolean isBehindNAT(String address) {
7449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            try {
7459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                byte[] d = InetAddress.getByName(address).getAddress();
7469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if ((d[0] == 10) ||
7479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        (((0x000000FF & ((int)d[0])) == 172) &&
7489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((0x000000F0 & ((int)d[1])) == 16)) ||
7499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        (((0x000000FF & ((int)d[0])) == 192) &&
7509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        ((0x000000FF & ((int)d[1])) == 168))) {
7519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    return true;
7529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
7539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } catch (UnknownHostException e) {
7549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.e(TAG, "isBehindAT()" + address, e);
7559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return false;
7579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void restart(int duration) {
7609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
7619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.cancel(this);
7629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mTimer.set(duration * 1000, this);
7639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private int backoffDuration() {
7669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int duration = SHORT_EXPIRY_TIME * mBackoff;
7679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (duration > 3600) {
7689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                duration = 3600;
7699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
7709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mBackoff *= 2;
7719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return duration;
7739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
7759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
7769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistering(ISipSession session) {
7779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
7789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
779d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
780d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
7819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mRegistered = false;
7829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistering(session);
7839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
7849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
7859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
786d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        private boolean notCurrentSession(ISipSession session) {
787d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            if (session != mSession) {
788d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                ((SipSessionGroup.SipSessionImpl) session).setListener(null);
789d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                return true;
790d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            }
791d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            return !mRunning;
792d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan        }
793d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
7949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
7959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationDone(ISipSession session, int duration) {
7969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
7979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
798d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
7999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationDone(session, duration);
8019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (duration > 0) {
8039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mSession.clearReRegisterRequired();
8049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = SystemClock.elapsedRealtime()
8059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (duration * 1000);
8069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (!mRegistered) {
8089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mRegistered = true;
8099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        // allow some overlap to avoid call drop during renew
8109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        duration -= MIN_EXPIRY_TIME;
8119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (duration < MIN_EXPIRY_TIME) {
8129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            duration = MIN_EXPIRY_TIME;
8139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
8149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        restart(duration);
8159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (isBehindNAT(mLocalIp) ||
8179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                mSession.getLocalProfile().getSendKeepAlive()) {
8189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            if (mKeepAliveProcess == null) {
8199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                mKeepAliveProcess =
8209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                        new KeepAliveProcess(mSession);
8219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            }
8229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            mKeepAliveProcess.start();
8239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
8249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
8259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
8269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mRegistered = false;
8279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mExpiryTime = -1L;
8289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "Refresh registration immediately");
8299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    run();
8309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
8319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
8359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationFailed(ISipSession session, int errorCode,
8369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                String message) {
8379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
8389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + SipErrorCode.toString(errorCode) + ": " + message);
8399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
840d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
8419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
8439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, "   pause auto-registration");
844d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                    stop();
845d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                } else {
8469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onError();
8479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
848d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
849d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorCode = errorCode;
850d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mErrorMessage = message;
851d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                mProxy.onRegistrationFailed(session, errorCode, message);
8529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
8569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onRegistrationTimeout(ISipSession session) {
8579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
8589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
859d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                if (notCurrentSession(session)) return;
860d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan
8619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mErrorCode = SipErrorCode.TIME_OUT;
8629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mProxy.onRegistrationTimeout(session);
863d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan                onError();
8649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onError() {
8689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mRegistered = false;
8699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            restart(backoffDuration());
8709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mKeepAliveProcess != null) {
8719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess.stop();
8729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mKeepAliveProcess = null;
8739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
8749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
8759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
8769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private class ConnectivityReceiver extends BroadcastReceiver {
8789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Timer mTimer = new Timer();
8799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private MyTimerTask mTask;
8809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
8829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void onReceive(Context context, Intent intent) {
8839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
8849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
8859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Bundle b = intent.getExtras();
8869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (b != null) {
8879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo netInfo = (NetworkInfo)
8889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            b.get(ConnectivityManager.EXTRA_NETWORK_INFO);
8899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    String type = netInfo.getTypeName();
8909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo.State state = netInfo.getState();
8919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
8929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mWifiOnly && (netInfo.getType() !=
8939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            ConnectivityManager.TYPE_WIFI)) {
8949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) {
8959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "Wifi only, other connectivity ignored: "
8969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + type);
8979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
8989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
8999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    NetworkInfo activeNetInfo = getActiveNetworkInfo();
9029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) {
9039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (activeNetInfo != null) {
9049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: "
9059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + activeNetInfo.getTypeName()
9069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                    + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED)
9079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                            ? " CONNECTED" : " DISCONNECTED"));
9089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        } else {
9099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            Log.d(TAG, "active network: null");
9109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        }
9119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((state == NetworkInfo.State.CONNECTED)
9139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo != null)
9149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            && (activeNetInfo.getType() != netInfo.getType())) {
9159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "ignore connect event: " + type
9169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + ", active: " + activeNetInfo.getTypeName());
9179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
9189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (state == NetworkInfo.State.CONNECTED) {
9219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: CONNECTED " + type);
9229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, true);
9239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else if (state == NetworkInfo.State.DISCONNECTED) {
9249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert: DISCONNECTED " + type);
9259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        onChanged(type, false);
9269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    } else {
9279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        if (DEBUG) Log.d(TAG, "Connectivity alert not processed: "
9289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + state + " " + type);
9299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private NetworkInfo getActiveNetworkInfo() {
9359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            ConnectivityManager cm = (ConnectivityManager)
9369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
9379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return cm.getActiveNetworkInfo();
9389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void onChanged(String type, boolean connected) {
9419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            synchronized (SipService.this) {
9429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // When turning on WIFI, it needs some time for network
9439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // connectivity to get stabile so we defer good news (because
9449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // we want to skip the interim ones) but deliver bad news
9459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // immediately
9469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (connected) {
9479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mTask != null) mTask.cancel();
9489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = new MyTimerTask(type, connected);
9499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTimer.schedule(mTask, 2 * 1000L);
9509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // TODO: hold wakup lock so that we can finish change before
9519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    // the device goes to sleep
9529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
9539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if ((mTask != null) && mTask.mNetworkType.equals(type)) {
9549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        mTask.cancel();
9559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(type, false);
9579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private class MyTimerTask extends TimerTask {
9629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private boolean mConnected;
9639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private String mNetworkType;
9649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public MyTimerTask(String type, boolean connected) {
9669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mNetworkType = type;
9679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mConnected = connected;
9689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
970d8d3b15f408314ac88201eee3e401a35556ba669Hung-ying Tyan            // timeout handler
9719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            @Override
9729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            public void run() {
9739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // delegate to mExecutor
9749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                getExecutor().addTask(new Runnable() {
9759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    public void run() {
9769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        realRun();
9779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                });
9799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            private void realRun() {
9829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                synchronized (SipService.this) {
9839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (mTask != this) {
9849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        Log.w(TAG, "  unexpected task: " + mNetworkType
9859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                                + (mConnected ? " CONNECTED" : "DISCONNECTED"));
9869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        return;
9879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    }
9889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mTask = null;
9899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG) Log.d(TAG, " deliver change for " + mNetworkType
9909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            + (mConnected ? " CONNECTED" : "DISCONNECTED"));
9919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    onConnectivityChanged(mNetworkType, mConnected);
9929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
9939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
9949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
9959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
9969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // TODO: clean up pending SipSession(s) periodically
9989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
9999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    /**
10019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Timer that can schedule events to occur even when the device is in sleep.
10029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     * Only used internally in this package.
10039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan     */
10049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    class WakeupTimer extends BroadcastReceiver {
10059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TAG = "_SIP.WkTimer_";
10069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static final String TRIGGER_TIME = "TriggerTime";
10079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private Context mContext;
10099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private AlarmManager mAlarmManager;
10109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // runnable --> time to execute in SystemClock
10129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private TreeSet<MyEvent> mEventQueue =
10139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new TreeSet<MyEvent>(new MyEventComparator());
10149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private PendingIntent mPendingIntent;
10169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public WakeupTimer(Context context) {
10189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mContext = context;
10199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager = (AlarmManager)
10209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    context.getSystemService(Context.ALARM_SERVICE);
10219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            IntentFilter filter = new IntentFilter(getAction());
10239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            context.registerReceiver(this, filter);
10249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
10279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Stops the timer. No event can be scheduled after this method is called.
10289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
10299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void stop() {
10309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mContext.unregisterReceiver(this);
10319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mPendingIntent != null) {
10329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mAlarmManager.cancel(mPendingIntent);
10339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPendingIntent = null;
10349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue.clear();
10369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue = null;
10379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private synchronized boolean stopped() {
10409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue == null) {
10419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "Timer stopped");
10429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return true;
10439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
10449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return false;
10459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void cancelAlarm() {
10499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager.cancel(mPendingIntent);
10509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPendingIntent = null;
10519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void recalculatePeriods() {
10549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) return;
10559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
10579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int minPeriod = firstEvent.mMaxPeriod;
10589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long minTriggerTime = firstEvent.mTriggerTime;
10599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent e : mEventQueue) {
10609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
10619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
10629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        - minTriggerTime);
10639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval = interval / minPeriod * minPeriod;
10649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                e.mTriggerTime = minTriggerTime + interval;
10659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
10679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    mEventQueue.comparator());
10689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            newQueue.addAll((Collection<MyEvent>) mEventQueue);
10699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue.clear();
10709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mEventQueue = newQueue;
10719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
10729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "queue re-calculated");
10739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
10749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
10769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
10779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // Determines the period and the trigger time of the new event and insert it
10789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        // to the queue.
10799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void insertEvent(MyEvent event) {
10809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long now = SystemClock.elapsedRealtime();
10819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) {
10829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime = now + event.mPeriod;
10839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
10849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                return;
10859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
10869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
10879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int minPeriod = firstEvent.mPeriod;
10889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (minPeriod <= event.mMaxPeriod) {
10899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
10909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                int interval = event.mMaxPeriod;
10919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval -= (int) (firstEvent.mTriggerTime - now);
10929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                interval = interval / minPeriod * minPeriod;
10939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime = firstEvent.mTriggerTime + interval;
10949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
10959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
10969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                long triggerTime = now + event.mPeriod;
10979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (firstEvent.mTriggerTime < triggerTime) {
10989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime = firstEvent.mTriggerTime;
10999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mLastTriggerTime -= event.mPeriod;
11009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                } else {
11019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime = triggerTime;
11029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
11039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mEventQueue.add(event);
11049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                recalculatePeriods();
11059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
11099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Sets a periodic timer.
11109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *
11119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param period the timer period; in milli-second
11129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param callback is called back when the timer goes off; the same callback
11139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *      can be specified in multiple timer events
11149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
11159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void set(int period, Runnable callback) {
11169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped()) return;
11179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long now = SystemClock.elapsedRealtime();
11199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent event = new MyEvent(period, callback, now);
11209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            insertEvent(event);
11219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.first() == event) {
11239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (mEventQueue.size() > 1) cancelAlarm();
11249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                scheduleNext();
11259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            long triggerTime = event.mTriggerTime;
11289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
11299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, " add event " + event + " scheduled at "
11309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(triggerTime) + " at " + showTime(now)
11319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + ", #events=" + mEventQueue.size());
11329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
11339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        /**
11379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * Cancels all the timer events with the specified callback.
11389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         *
11399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         * @param callback the callback
11409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan         */
11419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void cancel(Runnable callback) {
11429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
11439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback);
11449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent firstEvent = mEventQueue.first();
11469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (Iterator<MyEvent> iter = mEventQueue.iterator();
11479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    iter.hasNext();) {
11489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                MyEvent event = iter.next();
11499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (event.mCallback == callback) {
11509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    iter.remove();
11519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    if (DEBUG_TIMER) Log.d(TAG, "    cancel found:" + event);
11529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                }
11539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.isEmpty()) {
11559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                cancelAlarm();
11569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (mEventQueue.first() != firstEvent) {
11579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                cancelAlarm();
11589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent = mEventQueue.first();
11599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent.mPeriod = firstEvent.mMaxPeriod;
11609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
11619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + firstEvent.mPeriod;
11629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                recalculatePeriods();
11639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                scheduleNext();
11649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
11669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "after cancel:");
11679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
11689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void scheduleNext() {
11729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
11739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mPendingIntent != null) {
11759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                throw new RuntimeException("pendingIntent is not null!");
11769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            MyEvent event = mEventQueue.first();
11799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Intent intent = new Intent(getAction());
11809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
11819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            PendingIntent pendingIntent = mPendingIntent =
11829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    PendingIntent.getBroadcast(mContext, 0, intent,
11839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                            PendingIntent.FLAG_UPDATE_CURRENT);
11849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
11859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    event.mTriggerTime, pendingIntent);
11869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
11879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
11889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
11899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public synchronized void onReceive(Context context, Intent intent) {
11909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String action = intent.getAction();
11919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (getAction().equals(action)
11929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    && intent.getExtras().containsKey(TRIGGER_TIME)) {
11939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                mPendingIntent = null;
11949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
11959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                execute(triggerTime);
11969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
11979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "unrecognized intent: " + intent);
11989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
11999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void printQueue() {
12029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int count = 0;
12039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent event : mEventQueue) {
12049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     " + event + ": scheduled at "
12059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(event.mTriggerTime) + ": last at "
12069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        + showTime(event.mLastTriggerTime));
12079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (++count >= 5) break;
12089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (mEventQueue.size() > count) {
12109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     .....");
12119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else if (count == 0) {
12129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "     <empty>");
12139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private void execute(long triggerTime) {
12179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = "
12189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + showTime(triggerTime) + ": " + mEventQueue.size());
12199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (stopped() || mEventQueue.isEmpty()) return;
12209c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12219c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            for (MyEvent event : mEventQueue) {
12229c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (event.mTriggerTime != triggerTime) break;
12239c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                if (DEBUG_TIMER) Log.d(TAG, "execute " + event);
12249c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12259c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mLastTriggerTime = event.mTriggerTime;
12269c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                event.mTriggerTime += event.mPeriod;
12279c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12289c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                // run the callback in a new thread to prevent deadlock
12299c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                new Thread(event.mCallback, "SipServiceTimerCallbackThread")
12309c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                        .start();
12319c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12329c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (DEBUG_TIMER) {
12339c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.d(TAG, "after timeout execution");
12349c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                printQueue();
12359c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
12369c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            scheduleNext();
12379c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12389c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12399c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String getAction() {
12409c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return toString();
12419c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12429c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12439c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String showTime(long time) {
12449c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int ms = (int) (time % 1000);
12459c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int s = (int) (time / 1000);
12469c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int m = s / 60;
12479c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s %= 60;
12489c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return String.format("%d.%d.%d", m, s, ms);
12499c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12509c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12519c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12529c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static class MyEvent {
12539c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mPeriod;
12549c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        int mMaxPeriod;
12559c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        long mTriggerTime;
12569c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        long mLastTriggerTime;
12579c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        Runnable mCallback;
12589c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12599c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyEvent(int period, Runnable callback, long now) {
12609c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mPeriod = mMaxPeriod = period;
12619c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mCallback = callback;
12629c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            mLastTriggerTime = now;
12639c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12649c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12659c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
12669c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public String toString() {
12679c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String s = super.toString();
12689c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            s = s.substring(s.indexOf("@"));
12699c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
12709c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                    + toString(mCallback);
12719c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12729c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12739c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private String toString(Object o) {
12749c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            String s = o.toString();
12759c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int index = s.indexOf("$");
12769c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (index > 0) s = s.substring(index + 1);
12779c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return s;
12789c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12799c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12809c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12819c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static class MyEventComparator implements Comparator<MyEvent> {
12829c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public int compare(MyEvent e1, MyEvent e2) {
12839c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (e1 == e2) return 0;
12849c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            int diff = e1.mMaxPeriod - e2.mMaxPeriod;
12859c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (diff == 0) diff = -1;
12869c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return diff;
12879c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12889c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12899c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public boolean equals(Object that) {
12909c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return (this == that);
12919c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12929c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
12939c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
12949c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    // Single-threaded executor
12959c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    private static class MyExecutor extends Handler {
12969c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        MyExecutor() {
12979c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            super(createLooper());
12989c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
12999c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13009c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        private static Looper createLooper() {
13019c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            HandlerThread thread = new HandlerThread("SipService");
13029c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            thread.start();
13039c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            return thread.getLooper();
13049c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13059c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13069c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        void addTask(Runnable task) {
13079c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            Message.obtain(this, 0/* don't care */, task).sendToTarget();
13089c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13099c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan
13109c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        @Override
13119c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        public void handleMessage(Message msg) {
13129c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            if (msg.obj instanceof Runnable) {
13139c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                ((Runnable) msg.obj).run();
13149c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            } else {
13159c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan                Log.w(TAG, "can't handle msg: " + msg);
13169c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan            }
13179c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan        }
13189c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan    }
13199c1fbe7bca34ac7463079926a401a3ce42717460Hung-ying Tyan}
1320