FileDocCategorySizeDatePackage
SipService.javaAPI DocAndroid 5.1 API47575Thu Mar 12 22:22:52 GMT 2015com.android.server.sip

SipService

public final class SipService extends ISipService.Stub
hide

Fields Summary
static final String
TAG
static final boolean
DBG
private static final int
EXPIRY_TIME
private static final int
SHORT_EXPIRY_TIME
private static final int
MIN_EXPIRY_TIME
private static final int
DEFAULT_KEEPALIVE_INTERVAL
private static final int
DEFAULT_MAX_KEEPALIVE_INTERVAL
private android.content.Context
mContext
private String
mLocalIp
private int
mNetworkType
private SipWakeupTimer
mTimer
private WifiManager.WifiLock
mWifiLock
private boolean
mSipOnWifiOnly
private SipKeepAliveProcessCallback
mSipKeepAliveProcessCallback
private MyExecutor
mExecutor
private Map
mSipGroups
private Map
mPendingSessions
private ConnectivityReceiver
mConnectivityReceiver
private SipWakeLock
mMyWakeLock
private int
mKeepAliveInterval
private int
mLastGoodKeepAliveInterval
Constructors Summary
private SipService(android.content.Context context)

        if (DBG) log("SipService: started!");
        mContext = context;
        mConnectivityReceiver = new ConnectivityReceiver();

        mWifiLock = ((WifiManager)
                context.getSystemService(Context.WIFI_SERVICE))
                .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
        mWifiLock.setReferenceCounted(false);
        mSipOnWifiOnly = SipManager.isSipWifiOnly(context);

        mMyWakeLock = new SipWakeLock((PowerManager)
                context.getSystemService(Context.POWER_SERVICE));

        mTimer = new SipWakeupTimer(context, mExecutor);
    
Methods Summary
private synchronized voidaddPendingSession(android.net.sip.ISipSession session)

        try {
            cleanUpPendingSessions();
            mPendingSessions.put(session.getCallId(), session);
            if (DBG) log("#pending sess=" + mPendingSessions.size());
        } catch (RemoteException e) {
            // should not happen with a local call
            loge("addPendingSession()", e);
        }
    
private synchronized booleancallingSelf(com.android.server.sip.SipService$SipSessionGroupExt ringingGroup, SipSessionGroup.SipSessionImpl ringingSession)

        String callId = ringingSession.getCallId();
        for (SipSessionGroupExt group : mSipGroups.values()) {
            if ((group != ringingGroup) && group.containsSession(callId)) {
                if (DBG) log("call self: "
                        + ringingSession.getLocalProfile().getUriString()
                        + " -> " + group.getLocalProfile().getUriString());
                return true;
            }
        }
        return false;
    
private voidcleanUpPendingSessions()

        Map.Entry<String, ISipSession>[] entries =
                mPendingSessions.entrySet().toArray(
                new Map.Entry[mPendingSessions.size()]);
        for (Map.Entry<String, ISipSession> entry : entries) {
            if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
                mPendingSessions.remove(entry.getKey());
            }
        }
    
public synchronized voidclose(java.lang.String localProfileUri)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
        if (group == null) return;
        if (!isCallerCreatorOrRadio(group)) {
            if (DBG) log("only creator or radio can close this profile");
            return;
        }

        group = mSipGroups.remove(localProfileUri);
        notifyProfileRemoved(group.getLocalProfile());
        group.close();

        updateWakeLocks();
    
private com.android.server.sip.SipService$SipSessionGroupExtcreateGroup(android.net.sip.SipProfile localProfile)

        String key = localProfile.getUriString();
        SipSessionGroupExt group = mSipGroups.get(key);
        if (group == null) {
            group = new SipSessionGroupExt(localProfile, null, null);
            mSipGroups.put(key, group);
            notifyProfileAdded(localProfile);
        } else if (!isCallerCreator(group)) {
            throw new SipException("only creator can access the profile");
        }
        return group;
    
private com.android.server.sip.SipService$SipSessionGroupExtcreateGroup(android.net.sip.SipProfile localProfile, android.app.PendingIntent incomingCallPendingIntent, android.net.sip.ISipSessionListener listener)

        String key = localProfile.getUriString();
        SipSessionGroupExt group = mSipGroups.get(key);
        if (group != null) {
            if (!isCallerCreator(group)) {
                throw new SipException("only creator can access the profile");
            }
            group.setIncomingCallPendingIntent(incomingCallPendingIntent);
            group.setListener(listener);
        } else {
            group = new SipSessionGroupExt(localProfile,
                    incomingCallPendingIntent, listener);
            mSipGroups.put(key, group);
            notifyProfileAdded(localProfile);
        }
        return group;
    
private static android.os.LoopercreateLooper()

        HandlerThread thread = new HandlerThread("SipService.Executor");
        thread.start();
        return thread.getLooper();
    
public synchronized android.net.sip.ISipSessioncreateSession(android.net.sip.SipProfile localProfile, android.net.sip.ISipSessionListener listener)

        if (DBG) log("createSession: profile" + localProfile);
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        localProfile.setCallingUid(Binder.getCallingUid());
        if (mNetworkType == -1) {
            if (DBG) log("createSession: mNetworkType==-1 ret=null");
            return null;
        }
        try {
            SipSessionGroupExt group = createGroup(localProfile);
            return group.createSession(listener);
        } catch (SipException e) {
            if (DBG) loge("createSession;", e);
            return null;
        }
    
private java.lang.StringdetermineLocalIp()

        try {
            DatagramSocket s = new DatagramSocket();
            s.connect(InetAddress.getByName("192.168.1.1"), 80);
            return s.getLocalAddress().getHostAddress();
        } catch (IOException e) {
            if (DBG) loge("determineLocalIp()", e);
            // dont do anything; there should be a connectivity change going
            return null;
        }
    
private intgetKeepAliveInterval()

        return (mKeepAliveInterval < 0)
                ? mLastGoodKeepAliveInterval
                : mKeepAliveInterval;
    
public synchronized android.net.sip.SipProfile[]getListOfProfiles()

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        boolean isCallerRadio = isCallerRadio();
        ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
        for (SipSessionGroupExt group : mSipGroups.values()) {
            if (isCallerRadio || isCallerCreator(group)) {
                profiles.add(group.getLocalProfile());
            }
        }
        return profiles.toArray(new SipProfile[profiles.size()]);
    
public synchronized android.net.sip.ISipSessiongetPendingSession(java.lang.String callId)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        if (callId == null) return null;
        return mPendingSessions.get(callId);
    
private booleanisBehindNAT(java.lang.String address)

        try {
            // TODO: How is isBehindNAT used and why these constanst address:
            //       10.x.x.x | 192.168.x.x | 172.16.x.x .. 172.19.x.x
            byte[] d = InetAddress.getByName(address).getAddress();
            if ((d[0] == 10) ||
                    (((0x000000FF & d[0]) == 172) &&
                    ((0x000000F0 & d[1]) == 16)) ||
                    (((0x000000FF & d[0]) == 192) &&
                    ((0x000000FF & d[1]) == 168))) {
                return true;
            }
        } catch (UnknownHostException e) {
            loge("isBehindAT()" + address, e);
        }
        return false;
    
private booleanisCallerCreator(com.android.server.sip.SipService$SipSessionGroupExt group)

        SipProfile profile = group.getLocalProfile();
        return (profile.getCallingUid() == Binder.getCallingUid());
    
private booleanisCallerCreatorOrRadio(com.android.server.sip.SipService$SipSessionGroupExt group)

        return (isCallerRadio() || isCallerCreator(group));
    
private booleanisCallerRadio()

        return (Binder.getCallingUid() == Process.PHONE_UID);
    
public synchronized booleanisOpened(java.lang.String localProfileUri)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
        if (group == null) return false;
        if (isCallerCreatorOrRadio(group)) {
            return true;
        } else {
            if (DBG) log("only creator or radio can query on the profile");
            return false;
        }
    
public synchronized booleanisRegistered(java.lang.String localProfileUri)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
        if (group == null) return false;
        if (isCallerCreatorOrRadio(group)) {
            return group.isRegistered();
        } else {
            if (DBG) log("only creator or radio can query on the profile");
            return false;
        }
    
private voidlog(java.lang.String s)

        Rlog.d(TAG, s);
    
private voidloge(java.lang.String s, java.lang.Throwable e)

        Rlog.e(TAG, s, e);
    
private voidnotifyProfileAdded(android.net.sip.SipProfile localProfile)

        if (DBG) log("notify: profile added: " + localProfile);
        Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
        mContext.sendBroadcast(intent);
        if (mSipGroups.size() == 1) {
            registerReceivers();
        }
    
private voidnotifyProfileRemoved(android.net.sip.SipProfile localProfile)

        if (DBG) log("notify: profile removed: " + localProfile);
        Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
        intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
        mContext.sendBroadcast(intent);
        if (mSipGroups.size() == 0) {
            unregisterReceivers();
        }
    
private synchronized voidonConnectivityChanged(android.net.NetworkInfo info)

        // We only care about the default network, and getActiveNetworkInfo()
        // is the only way to distinguish them. However, as broadcasts are
        // delivered asynchronously, we might miss DISCONNECTED events from
        // getActiveNetworkInfo(), which is critical to our SIP stack. To
        // solve this, if it is a DISCONNECTED event to our current network,
        // respect it. Otherwise get a new one from getActiveNetworkInfo().
        if (info == null || info.isConnected() || info.getType() != mNetworkType) {
            ConnectivityManager cm = (ConnectivityManager)
                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
            info = cm.getActiveNetworkInfo();
        }

        // Some devices limit SIP on Wi-Fi. In this case, if we are not on
        // Wi-Fi, treat it as a DISCONNECTED event.
        int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
        if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
            networkType = -1;
        }

        // Ignore the event if the current active network is not changed.
        if (mNetworkType == networkType) {
            // TODO: Maybe we need to send seq/generation number
            return;
        }
        if (DBG) {
            log("onConnectivityChanged: " + mNetworkType +
                    " -> " + networkType);
        }

        try {
            if (mNetworkType != -1) {
                mLocalIp = null;
                stopPortMappingMeasurement();
                for (SipSessionGroupExt group : mSipGroups.values()) {
                    group.onConnectivityChanged(false);
                }
            }
            mNetworkType = networkType;

            if (mNetworkType != -1) {
                mLocalIp = determineLocalIp();
                mKeepAliveInterval = -1;
                mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
                for (SipSessionGroupExt group : mSipGroups.values()) {
                    group.onConnectivityChanged(true);
                }
            }
            updateWakeLocks();
        } catch (SipException e) {
            loge("onConnectivityChanged()", e);
        }
    
private synchronized voidonKeepAliveIntervalChanged()

        for (SipSessionGroupExt group : mSipGroups.values()) {
            group.onKeepAliveIntervalChanged();
        }
    
public synchronized voidopen(android.net.sip.SipProfile localProfile)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        localProfile.setCallingUid(Binder.getCallingUid());
        try {
            createGroup(localProfile);
        } catch (SipException e) {
            loge("openToMakeCalls()", e);
            // TODO: how to send the exception back
        }
    
public synchronized voidopen3(android.net.sip.SipProfile localProfile, android.app.PendingIntent incomingCallPendingIntent, android.net.sip.ISipSessionListener listener)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        localProfile.setCallingUid(Binder.getCallingUid());
        if (incomingCallPendingIntent == null) {
            if (DBG) log("open3: incomingCallPendingIntent cannot be null; "
                    + "the profile is not opened");
            return;
        }
        if (DBG) log("open3: " + localProfile.getUriString() + ": "
                + incomingCallPendingIntent + ": " + listener);
        try {
            SipSessionGroupExt group = createGroup(localProfile,
                    incomingCallPendingIntent, listener);
            if (localProfile.getAutoRegistration()) {
                group.openToReceiveCalls();
                updateWakeLocks();
            }
        } catch (SipException e) {
            loge("open3:", e);
            // TODO: how to send the exception back
        }
    
private voidregisterReceivers()

        mContext.registerReceiver(mConnectivityReceiver,
                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        if (DBG) log("registerReceivers:");
    
private voidrestartPortMappingLifetimeMeasurement(android.net.sip.SipProfile localProfile, int maxInterval)

        stopPortMappingMeasurement();
        mKeepAliveInterval = -1;
        startPortMappingLifetimeMeasurement(localProfile, maxInterval);
    
public synchronized voidsetRegistrationListener(java.lang.String localProfileUri, android.net.sip.ISipSessionListener listener)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        SipSessionGroupExt group = mSipGroups.get(localProfileUri);
        if (group == null) return;
        if (isCallerCreator(group)) {
            group.setListener(listener);
        } else {
            if (DBG) log("only creator can set listener on the profile");
        }
    
private static voidslog(java.lang.String s)

        Rlog.d(TAG, s);
    
public static voidstart(android.content.Context context)
Starts the SIP service. Do nothing if the SIP API is not supported on the device.


                         
         
        if (SipManager.isApiSupported(context)) {
            ServiceManager.addService("sip", new SipService(context));
            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
            if (DBG) slog("start:");
        }
    
private voidstartPortMappingLifetimeMeasurement(android.net.sip.SipProfile localProfile)

        startPortMappingLifetimeMeasurement(localProfile,
                DEFAULT_MAX_KEEPALIVE_INTERVAL);
    
private voidstartPortMappingLifetimeMeasurement(android.net.sip.SipProfile localProfile, int maxInterval)

        if ((mSipKeepAliveProcessCallback == null)
                && (mKeepAliveInterval == -1)
                && isBehindNAT(mLocalIp)) {
            if (DBG) log("startPortMappingLifetimeMeasurement: profile="
                    + localProfile.getUriString());

            int minInterval = mLastGoodKeepAliveInterval;
            if (minInterval >= maxInterval) {
                // If mLastGoodKeepAliveInterval also does not work, reset it
                // to the default min
                minInterval = mLastGoodKeepAliveInterval
                        = DEFAULT_KEEPALIVE_INTERVAL;
                log("  reset min interval to " + minInterval);
            }
            mSipKeepAliveProcessCallback = new SipKeepAliveProcessCallback(
                    localProfile, minInterval, maxInterval);
            mSipKeepAliveProcessCallback.start();
        }
    
private voidstopPortMappingMeasurement()

        if (mSipKeepAliveProcessCallback != null) {
            mSipKeepAliveProcessCallback.stop();
            mSipKeepAliveProcessCallback = null;
        }
    
private voidunregisterReceivers()

        mContext.unregisterReceiver(mConnectivityReceiver);
        if (DBG) log("unregisterReceivers:");

        // Reset variables maintained by ConnectivityReceiver.
        mWifiLock.release();
        mNetworkType = -1;
    
private voidupdateWakeLocks()

        for (SipSessionGroupExt group : mSipGroups.values()) {
            if (group.isOpenedToReceiveCalls()) {
                // Also grab the WifiLock when we are disconnected, so the
                // system will keep trying to reconnect. It will be released
                // when the system eventually connects to something else.
                if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) {
                    mWifiLock.acquire();
                } else {
                    mWifiLock.release();
                }
                return;
            }
        }
        mWifiLock.release();
        mMyWakeLock.reset(); // in case there's a leak