FileDocCategorySizeDatePackage
ImpsSession.javaAPI DocAndroid 1.5 API15426Wed May 06 22:42:46 BST 2009com.android.im.imps

ImpsSession.java

/*
 * Copyright (C) 2007 Esmertec AG.
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.android.im.imps;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.android.im.engine.Contact;
import com.android.im.engine.ImErrorInfo;
import com.android.im.engine.ImException;
import com.android.im.engine.LoginInfo;
import com.android.im.imps.ImpsConnectionConfig.CirMethod;
import com.android.im.imps.ImpsConnectionConfig.TransportType;

/**
 * Represents the context of an IMPS session. The IMPS session is a framework in
 * which the IMPS services are provided to the IMPS client. It's established
 * when the client logs in and terminated when either the client logs out or the
 * SAP decides to disconnect the session.
 */
public class ImpsSession {
    private static final String KEY_CIR_HTTP_ADDRESS = "cirHttpAddress";
    private static final String KEY_CIR_TCP_PORT = "cirTcpPort";
    private static final String KEY_CIR_TCP_ADDRESS = "cirTcpAddress";
    private static final String KEY_CIR_METHOD = "CirMethod";
    private static final String KEY_SERVER_POLL_MIN = "serverPollMin";
    private static final String KEY_PASSWORD = "password";
    private static final String KEY_USERNAME = "username";
    private static final String KEY_KEEP_ALIVE_TIME = "keepAliveTime";
    private static final String KEY_SESSION_COOKIE = "sessionCookie";
    private static final String KEY_SESSION_ID = "sessionId";

    private static final int DEFAULT_TCP_PORT = 3171;

    private ImpsConnection mConnection;
    private String mId;
    private String mCookie;
    private long mKeepAliveTime;
    private CirMethod mCurrentCirMethod;
    private String mCirTcpAddress;
    private int mCirTcpPort = DEFAULT_TCP_PORT;
    private long mServerPollMin;
    private String mCirHttpAddress;
    private LoginInfo mLoginInfo;

    private boolean mCapablityRequest;
    private List<CirMethod> mSupportedCirMethod;

    private Contact mLoginUser;

    PrimitiveElement mServiceTree;

    /**
     * Flag that indicates this is a new created session or not.
     */
    private boolean mNew;

    ImpsSession(ImpsConnection connection, LoginInfo info) throws ImException{
        mConnection = connection;
        setLoginInfo(info);

        mNew = true;
        mCookie = ImpsUtils.genSessionCookie();

        mCirTcpPort = DEFAULT_TCP_PORT;
        mServerPollMin = connection.getConfig().getDefaultServerPollMin();
    }

    ImpsSession(ImpsConnection connection, HashMap<String, String> values)
            throws ImException {
        mConnection = connection;
        mNew = false;
        mId = values.get(KEY_SESSION_ID);
        if (mId == null || mId.length() == 0) {
            throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                "Missing session id");
        }
        mCookie = values.get(KEY_SESSION_COOKIE);
        if (mCookie == null || mCookie.length() == 0) {
            throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                "Missing session cookie");
        }
        try {
            mKeepAliveTime = Long.parseLong(values.get(KEY_KEEP_ALIVE_TIME));
        } catch (NumberFormatException e) {
            throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                "Invalid keepAliveTime");
        }
        try {
            mServerPollMin = Long.parseLong(values.get(KEY_SERVER_POLL_MIN));
        } catch (NumberFormatException e) {
            throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                "Invalid serverPollMin");
        }
        String username = values.get(KEY_USERNAME);
        String password = values.get(KEY_PASSWORD);
        // Empty password might be valid
        if (username == null || username.length() == 0 || password == null) {
            throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                "Invalid username or password");
        }
        setLoginInfo(new LoginInfo(username, password));

        mCurrentCirMethod = CirMethod.valueOf(values.get(KEY_CIR_METHOD));
        if (mCurrentCirMethod == CirMethod.STCP) {
            mCirTcpAddress = values.get(KEY_CIR_TCP_ADDRESS);
            if (mCirTcpAddress == null || mCirTcpAddress.length() == 0) {
                throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                    "Missing CirTcpAddress");
            }
            try {
                mCirTcpPort = Integer.parseInt(values.get(KEY_CIR_TCP_PORT));
            } catch (NumberFormatException e) {
                throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
                    "Invalid CirTcpPort");
            }
        } else if (mCurrentCirMethod == CirMethod.SHTTP) {
            mCirHttpAddress = values.get(KEY_CIR_HTTP_ADDRESS);
        }
    }

    public HashMap<String, String> getContext() {
        HashMap<String, String> values = new HashMap<String, String>();

        values.put(KEY_SESSION_ID, mId);
        values.put(KEY_SESSION_COOKIE, mCookie);
        values.put(KEY_KEEP_ALIVE_TIME, Long.toString(mKeepAliveTime));
        values.put(KEY_USERNAME, mLoginInfo.getUserName());
        values.put(KEY_PASSWORD, mLoginInfo.getPassword());
        values.put(KEY_SERVER_POLL_MIN, Long.toString(mServerPollMin));

        values.put(KEY_CIR_METHOD, mCurrentCirMethod.name());
        if(mCurrentCirMethod == CirMethod.STCP) {
            values.put(KEY_CIR_TCP_ADDRESS, mCirTcpAddress);
            values.put(KEY_CIR_TCP_PORT, Integer.toString(mCirTcpPort));
        } else if (mCurrentCirMethod == CirMethod.SHTTP) {
            values.put(KEY_CIR_HTTP_ADDRESS, mCirHttpAddress);
        }
        return values;
    }

    /**
     * Gets the unique id of the session.
     *
     * @return the unique id of the session.
     */
    public String getID() {
        return mId;
    }

    public void setId(String id) {
        mId = id;
    }

    public String getCookie() {
        return mCookie;
    }

    public String getUserName() {
        return mLoginInfo.getUserName();
    }

    public String getPassword() {
        return mLoginInfo.getPassword();
    }

    public LoginInfo getLoginInfo() {
        return mLoginInfo;
    }
    /**
     * Gets the auto logout timer value.
     *
     * @return the auto logout timer value.
     */
    public long getKeepAliveTime() {
        return mKeepAliveTime;
    }

    public void setKeepAliveTime(long keepAliveTime) {
        mKeepAliveTime = keepAliveTime;
    }

    /**
     * Gets if further capability request is required in the session.
     *
     * @return <code>true</code> if further capability request is required.
     */
    public boolean isCapablityRequestRequired() {
        return mCapablityRequest || mNew;
    }

    public void setCapablityRequestRequired(boolean required) {
        mCapablityRequest = required;
    }

    public ImpsUserAddress getLoginUserAddress() {
        return (ImpsUserAddress) mLoginUser.getAddress();
    }

    public Contact getLoginUser() {
        return mLoginUser;
    }

    /**
     * Sets the Login information. After login successfully, the login
     * information should be saved in the session context so that we can auto
     * login when reconnect to the server.
     *
     * @param loginInfo the login information.
     * @throws ImException
     */
    private void setLoginInfo(LoginInfo loginInfo) throws ImException {
        try {
            ImpsAddress address = new ImpsUserAddress(loginInfo.getUserName());
            mLoginUser = new Contact(address, address.getScreenName());
            mLoginInfo = loginInfo;
        } catch (IllegalArgumentException e) {
            throw new ImException(ImErrorInfo.INVALID_USERNAME,
                    "Invalid username");
        }
    }

    /**
     * Gets a collection of CIR methods that are supported by both the client
     * and the server.
     *
     * @return a collection of supported CIR methods
     */
    public List<CirMethod> getSupportedCirMethods() {
        return mSupportedCirMethod;
    }

    public CirMethod getCurrentCirMethod() {
        return mCurrentCirMethod;
    }

    public void setCurrentCirMethod(CirMethod cirMethod) {
        mCurrentCirMethod = cirMethod;
    }

    /**
     * Gets the IP address for standalone TCP/IP CIR method.
     *
     * @return the IP address for standalone TCP/IP CIR method
     */
    public String getCirTcpAddress() {
        return mCirTcpAddress;
    }

    /**
     * Gets the port number for the standalone TCP/IP CIR method.
     *
     * @return the port number for the standalone TCP/IP CIR method.
     */
    public int getCirTcpPort() {
        return mCirTcpPort;
    }

    /**
     * Gets the minimum time interval (in seconds) that MUST pass before two
     * subsequent PollingRequest transactions.
     *
     * @return the minimum time interval in seconds.
     */
    public long getServerPollMin() {
        return mServerPollMin;
    }

    /**
     * Gets the URL used for standalone HTTP binding of CIR channel.
     *
     * @return the URL.
     */
    public String getCirHttpAddress() {
        return mCirHttpAddress;
    }

    /**
     * Gets the service tree of the features and functions that the server
     * supports.
     *
     * @return the service tree.
     */
    public PrimitiveElement getServiceTree() {
        return mServiceTree;
    }

    /**
     * Perform client capability negotiation with the server asynchronously.
     *
     * @param completion Async completion object.
     */
    public void negotiateCapabilityAsync(AsyncCompletion completion) {
        Primitive capabilityRequest = buildCapabilityRequest();

        AsyncTransaction tx = new AsyncTransaction(
                mConnection.getTransactionManager(), completion) {

            @Override
            public void onResponseOk(Primitive response) {
                extractCapability(response);
            }

            @Override
            public void onResponseError(ImpsErrorInfo error) { }
        };

        tx.sendRequest(capabilityRequest);
    }

    /**
     * Perform service negotiation with the server asynchronously.
     *
     * @param completion Async completion object.
     */
    public void negotiateServiceAsync(AsyncCompletion completion) {
        Primitive serviceRequest = buildServiceRequest();
        AsyncTransaction tx = new AsyncTransaction(
                mConnection.getTransactionManager(), completion) {

            @Override
            public void onResponseOk(Primitive response) {
                mServiceTree = response.getElement(ImpsTags.AllFunctions).getFirstChild();
            }

            @Override
            public void onResponseError(ImpsErrorInfo error) { }
        };

        tx.sendRequest(serviceRequest);
    }

    private Primitive buildCapabilityRequest() {
        Primitive capabilityRequest = new Primitive(ImpsTags.ClientCapability_Request);
        PrimitiveElement list = capabilityRequest.addElement(ImpsTags.CapabilityList);
        list.addChild(ImpsTags.ClientType, ImpsClientCapability.getClientType());
        list.addChild(ImpsTags.AcceptedContentLength, Integer
                .toString(ImpsClientCapability.getAcceptedContentLength()));
        list.addChild(ImpsTags.ParserSize,
                Integer.toString(ImpsClientCapability.getParserSize()));
        list.addChild(ImpsTags.MultiTrans,
                Integer.toString(ImpsClientCapability.getMultiTrans()));

        // TODO: MultiTransPerMessage is IMPS 1.3
        //list.addChild(ImpsTags.MultiTransPerMessage,
        //        Integer.toString(ImpsClientCapability.getMultiTransPerMessage()));
        list.addChild(ImpsTags.InitialDeliveryMethod,
                ImpsClientCapability.getInitialDeliveryMethod());
        list.addChild(ImpsTags.ServerPollMin, Long.toString(mServerPollMin));

        for(TransportType supportedBear : ImpsClientCapability.getSupportedBearers()) {
            list.addChild(ImpsTags.SupportedBearer, supportedBear.toString());
        }

        for(CirMethod supportedCirMethod : ImpsClientCapability.getSupportedCirMethods()) {
            list.addChild(ImpsTags.SupportedCIRMethod, supportedCirMethod.toString());
            if (CirMethod.SUDP.equals(supportedCirMethod)) {
                list.addChild(ImpsTags.UDPPort,
                        Integer.toString(mConnection.getConfig().getUdpPort()));
            }
        }

        return capabilityRequest;
    }

    /* keep this method package private instead of private to avoid the
     * overhead of calling from a inner class.
     */
    void extractCapability(Primitive capabilityResponse) {
        mSupportedCirMethod = new ArrayList<CirMethod>();

        PrimitiveElement agreedList = capabilityResponse.getContentElement()
                .getFirstChild();
        for (PrimitiveElement element : agreedList.getChildren()) {
            String tag = element.getTagName();
            if (tag.equals(ImpsTags.SupportedCIRMethod)) {
                try {
                    mSupportedCirMethod.add(CirMethod.valueOf(element.getContents()));
                } catch (IllegalArgumentException e) {
                    ImpsLog.log("Unrecognized CIR method " + element.getContents());
                }
            } else if (tag.equals(ImpsTags.TCPAddress)) {
                mCirTcpAddress = element.getContents();
            } else if (tag.equals(ImpsTags.TCPPort)) {
                mCirTcpPort = (int)ImpsUtils.parseLong(element.getContents(),
                        DEFAULT_TCP_PORT);
            } else if (tag.equals(ImpsTags.ServerPollMin)) {
                long defaultPollMin = mConnection.getConfig().getDefaultServerPollMin();
                mServerPollMin = ImpsUtils.parseLong(element.getContents(),
                        defaultPollMin);
                if (mServerPollMin <= 0) {
                    mServerPollMin = defaultPollMin;
                }
            } else if (tag.equals(ImpsTags.CIRHTTPAddress)
                    || tag.equals(ImpsTags.CIRURL)) {
                // This tag is CIRHTTPAddress in 1.3 and CIRURL in 1.2
                mCirHttpAddress = element.getChildContents(ImpsTags.URL);
            }
        }
    }

    private Primitive buildServiceRequest() {
        Primitive serviceRequest = new Primitive(ImpsTags.Service_Request);
        PrimitiveElement functions = serviceRequest.addElement(ImpsTags.Functions);
        PrimitiveElement features = functions.addChild(ImpsTags.WVCSPFeat);
        features.addChild(ImpsTags.FundamentalFeat);
        features.addChild(ImpsTags.PresenceFeat);
        features.addChild(ImpsTags.IMFeat);
        features.addChild(ImpsTags.GroupFeat);
        serviceRequest.addElement(ImpsTags.AllFunctionsRequest, true);
        return serviceRequest;
    }
}