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

ImpsConnection

public class ImpsConnection extends com.android.im.engine.ImConnection
An implementation of ImConnection of Wireless Village IMPS protocol.

Fields Summary
ImpsConnectionConfig
mConfig
DataChannel
mDataChannel
private CirChannel
mCirChannel
private PrimitiveDispatcherThread
mDispatcherThread
ImpsSession
mSession
ImpsTransactionManager
mTransactionManager
private ImpsChatSessionManager
mChatSessionManager
private ImpsContactListManager
mContactListManager
private ImpsChatGroupManager
mChatGroupManager
private boolean
mReestablishing
Constructors Summary
public ImpsConnection(ImpsConnectionConfig config)
Constructs a new WVConnection with a WVConnectionConfig object.

param
config the configuration.
throws
ImException if there's an error in the configuration.

        super();

        mConfig = config;

        mTransactionManager = new ImpsTransactionManager(this);
        mChatSessionManager = new ImpsChatSessionManager(this);
        mContactListManager = new ImpsContactListManager(this);
        mChatGroupManager   = new ImpsChatGroupManager(this);
    
Methods Summary
PrimitivebuildBasicLoginReq()

        Primitive login = new Primitive(ImpsTags.Login_Request);
        login.addElement(ImpsTags.UserID, mSession.getUserName());
        PrimitiveElement clientId = login.addElement(ImpsTags.ClientID);
        clientId.addChild(ImpsTags.URL, mConfig.getClientId());
        if (mConfig.getMsisdn() != null) {
            clientId.addChild(ImpsTags.MSISDN, mConfig.getMsisdn());
        }
        // we request for a bigger TimeToLive value than our default keep
        // alive interval to make sure we always have time to send the keep
        // alive requests.
        login.addElement(ImpsTags.TimeToLive,
                Integer.toString(mConfig.getDefaultKeepAliveInterval() + 5));
        login.addElement(ImpsTags.SessionCookie, mSession.getCookie());
        return login;
    
private PrimitiveElementbuildClientInfoElem()

        PrimitiveElement clientInfo = new PrimitiveElement(ImpsTags.ClientInfo);
        clientInfo.addChild(ImpsTags.Qualifier, true);

        Map<String, String> map = ImpsUtils.getClientInfo();
        for (Map.Entry<String, String> item : map.entrySet()) {
            clientInfo.addChild(item.getKey(), item.getValue());
        }

        return clientInfo;
    
private PrimitivebuildUpdatePresenceReq(PrimitiveElement presence)

        ArrayList<PrimitiveElement> presences = new ArrayList<PrimitiveElement>();

        presences.add(presence);

        return buildUpdatePresenceReq(presences);
    
private PrimitivebuildUpdatePresenceReq(java.util.ArrayList presences)

        Primitive updatePresenceRequest = new Primitive(ImpsTags.UpdatePresence_Request);

        PrimitiveElement presenceSubList = updatePresenceRequest
                .addElement(ImpsTags.PresenceSubList);
        presenceSubList.setAttribute(ImpsTags.XMLNS, mConfig.getPresenceNs());

        for (PrimitiveElement presence : presences) {
            presenceSubList.addChild(presence);
        }

        return updatePresenceRequest;
    
private synchronized booleancheckAndSetState(int state)

        if(mState != state){
            return false;
        }
        setState(LOGGING_IN, null);
        return true;
    
private DataChannelcreateDataChannel()

        TransportType dataChannelBinding = mConfig.getDataChannelBinding();
        if (dataChannelBinding == TransportType.HTTP) {
            return new HttpDataChannel(this);
        } else if (dataChannelBinding == TransportType.SMS) {
            return new SmsDataChannel(this);
        } else {
            throw new ImException("Unsupported data channel binding");
        }
    
private voiddoLogin()

        try {
            if (mConfig.useSmsAuth()) {
                mDataChannel = new SmsDataChannel(this);
            } else {
                mDataChannel = createDataChannel();
            }
            mDataChannel.connect();
        } catch (ImException e) {
            ImErrorInfo error = e.getImError();
            if(error == null){
                error = new ImErrorInfo(ImErrorInfo.UNKNOWN_LOGIN_ERROR,
                        e.getMessage());
            }
            shutdownOnError(error);
            return;
        }

        mDispatcherThread = new PrimitiveDispatcherThread(mDataChannel);
        mDispatcherThread.start();

        LoginTransaction login = new LoginTransaction();
        login.startAuthenticate();
    
protected voiddoUpdateUserPresenceAsync(com.android.im.engine.Presence presence)

        ArrayList<PrimitiveElement> presenceSubList = ImpsPresenceUtils.buildUpdatePresenceElems(
                mUserPresence, presence, mConfig.getPresenceMapping());
        Primitive request = buildUpdatePresenceReq(presenceSubList);
        // Need to make a copy because the presence passed in may change
        // before the transaction finishes.
        final Presence newPresence = new Presence(presence);

        AsyncTransaction tx = new AsyncTransaction(mTransactionManager) {

            @Override
            public void onResponseOk(Primitive response) {
                savePresenceChange(newPresence);
                notifyUserPresenceUpdated();
            }

            @Override
            public void onResponseError(ImpsErrorInfo error) {
                notifyUpdateUserPresenceError(error);
            }
        };
        tx.sendRequest(request);
    
public intgetCapability()

        return CAPABILITY_GROUP_CHAT | CAPABILITY_SESSION_REESTABLISHMENT;
    
public com.android.im.engine.ChatGroupManagergetChatGroupManager()

        return mChatGroupManager;
    
public com.android.im.engine.ChatSessionManagergetChatSessionManager()

        return mChatSessionManager;
    
ImpsConnectionConfiggetConfig()
Gets the configuration of this connection.

return
the configuration.

        return mConfig;
    
public com.android.im.engine.ContactListManagergetContactListManager()

        return mContactListManager;
    
public com.android.im.engine.ContactgetLoginUser()

        if(mSession == null){
            return null;
        }
        Contact loginUser = mSession.getLoginUser();
        loginUser.setPresence(getUserPresence());
        return loginUser;
    
public ImpsSessiongetSession()

        return mSession;
    
public java.util.HashMapgetSessionContext()

        if(mState != LOGGED_IN) {
            return null;
        } else {
            return mSession.getContext();
        }
    
public int[]getSupportedPresenceStatus()

        return mConfig.getPresenceMapping().getSupportedPresenceStatus();
    
public ImpsTransactionManagergetTransactionManager()

        return mTransactionManager;
    
public voidloginAsync(com.android.im.engine.LoginInfo loginInfo)

        if (!checkAndSetState(DISCONNECTED)) {
            return;
        }
        try {
            mSession = new ImpsSession(this, loginInfo);
        } catch (ImException e) {
            setState(DISCONNECTED, e.getImError());
            return;
        }
        doLogin();
    
public voidlogoutAsync()

        setState(LOGGING_OUT, null);
        // Shutdown the CIR channel first.
        if(mCirChannel != null) {
            mCirChannel.shutdown();
            mCirChannel = null;
        }

        // Only send the Logout-Request if the session has been established.
        if (mSession.getID() != null) {
            sendLogoutRequest();
        }
    
public voidnetworkTypeChanged()

        if (mCirChannel != null) {
            mCirChannel.reconnect();
        }
    
voidprocessIncomingPrimitive(Primitive primitive)
Handles the primitive received from the server.

param
primitive the received primitive.

        // if CIR is 'F', the CIR channel is not available. Re-establish it.
        if (primitive.getCir() != null && ImpsUtils.isFalse(primitive.getCir())) {
            if(mCirChannel != null) {
                mCirChannel.shutdown();
            }
            try {
                setupCIRChannel();
            } catch (ImException e) {
                e.printStackTrace();
            }
        }

        if (primitive.getPoll() != null && ImpsUtils.isTrue(primitive.getPoll())) {
            sendPollingRequest();
        }

        if (primitive.getType().equals(ImpsTags.Disconnect)) {
            if (mState != LOGGING_OUT) {
                ImErrorInfo error = ImpsUtils.checkResultError(primitive);
                shutdownOnError(error);
                return;
            }
        }

        if (primitive.getTransactionMode() == TransactionMode.Response) {
            ImpsErrorInfo error = ImpsUtils.checkResultError(primitive);
            if (error != null) {
                int code = error.getCode();
                if (code == ImpsErrorInfo.SESSION_EXPIRED
                        || code == ImpsErrorInfo.FORCED_LOGOUT
                        || code == ImpsErrorInfo.INVALID_SESSION) {
                    shutdownOnError(error);
                    return;
                }
            }
        }

        // According to the IMPS spec, only VersionDiscoveryResponse which
        // are not supported now doesn't have a transaction ID.
        if (primitive.getTransactionID() != null) {
            mTransactionManager.notifyIncomingPrimitive(primitive);
        }
    
public voidreestablishSessionAsync(java.util.HashMap cookie)

        if (!checkAndSetState(SUSPENDED)) {
            return;
        }
        // If we can resume from the data channel, which means the
        // session is still valid, we can just re-use the existing
        // session and don't need to re-establish it.
        if (mDataChannel.resume()) {
            try {
                setupCIRChannel();
            } catch(ImException e) {}
            setState(LOGGED_IN, null);
        } else {
            // Failed to resume the data channel which means the
            // session might have expired, we need to re-establish
            // the session by signing in again.
            mReestablishing = true;
            try {
                mSession = new ImpsSession(this, cookie);
            } catch (ImException e) {
                setState(DISCONNECTED, e.getImError());
                return;
            }
            doLogin();
        }
    
voidretrieveUserPresenceAsync(AsyncCompletion completion)

        Primitive request = new Primitive(ImpsTags.GetPresence_Request);

        request.addElement(this.getSession().getLoginUserAddress().toPrimitiveElement());
        AsyncTransaction tx = new AsyncTransaction(mTransactionManager){

            @Override
            public void onResponseOk(Primitive response) {
                PrimitiveElement presence = response.getElement(ImpsTags.Presence);
                PrimitiveElement presenceSubList = presence.getChild(ImpsTags.PresenceSubList);
                mUserPresence = ImpsPresenceUtils.extractPresence(presenceSubList,
                        mConfig.getPresenceMapping());
                // XXX: workaround for the OZ IMPS GTalk server that
                // returns an initial 'F' OnlineStatus. Set the online
                // status to available in this case.
                if(mUserPresence.getStatus() == Presence.OFFLINE) {
                    mUserPresence.setStatus(Presence.AVAILABLE);
                }
                compareAndUpdateClientInfo();
            }

            @Override
            public void onResponseError(ImpsErrorInfo error) {
                mUserPresence = new Presence(Presence.AVAILABLE, "", null,
                        null, Presence.CLIENT_TYPE_MOBILE, ImpsUtils.getClientInfo());
                completion.onError(error);
            }

            private void compareAndUpdateClientInfo() {
                if (!ImpsUtils.getClientInfo().equals(mUserPresence.getExtendedInfo())) {
                    updateClientInfoAsync(completion);
                    return;
                }
                // no need to update our client info to the server again
                completion.onComplete();
            }
        };

        tx.sendRequest(request);
    
voidsavePresenceChange(com.android.im.engine.Presence newPresence)

        mUserPresence.setStatusText(newPresence.getStatusText());
        mUserPresence.setStatus(newPresence.getStatus());
        mUserPresence.setAvatar(newPresence.getAvatarData(), newPresence.getAvatarType());
        // no need to update extended info because it's always read only.
    
voidsendLogoutRequest()

        // We cannot shut down our connections in ImpsAsyncTransaction.onResponse()
        // because at that time the logout transaction itself hasn't ended yet. So
        // we have to do this in this completion object.
        AsyncCompletion completion = new AsyncCompletion() {
            public void onComplete() {
                shutdown();
            }

            public void onError(ImErrorInfo error) {
                // We simply ignore all errors when logging out.
                // NowIMP responds a <Disconnect> instead of <Status> on logout request.
                shutdown();
            }
        };
        AsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager,
                completion);
        Primitive logoutPrimitive = new Primitive(ImpsTags.Logout_Request);
        tx.sendRequest(logoutPrimitive);
    
voidsendPollingRequest()
Sends a PollingRequest to the server.

        Primitive pollingRequest = new Primitive(ImpsTags.Polling_Request);
        pollingRequest.setSession(getSession().getID());
        mDataChannel.sendPrimitive(pollingRequest);
    
voidsendPrimitive(Primitive primitive)
Sends a specific primitive to the server. It will return immediately after the primitive has been put to the sending queue.

param
primitive the packet to send.

        mDataChannel.sendPrimitive(primitive);
    
voidsetupCIRChannel()

        if(mConfig.getDataChannelBinding() == TransportType.SMS) {
            // No CIR channel is needed, do nothing.
            return;
        }
        CirMethod cirMethod = mSession.getCurrentCirMethod();
        if (cirMethod == null) {
            cirMethod = mConfig.getCirChannelBinding();

            if (!mSession.getSupportedCirMethods().contains(cirMethod)) {
                // Sever don't support the CIR method
                cirMethod = CirMethod.SHTTP;
            }
            mSession.setCurrentCirMethod(cirMethod);
        }

        if (cirMethod == CirMethod.SHTTP) {
            mCirChannel = new HttpCirChannel(this, mDataChannel);
        } else if (cirMethod == CirMethod.STCP) {
            mCirChannel = new TcpCirChannel(this);
        } else if (cirMethod == CirMethod.SSMS) {
            mCirChannel = new SmsCirChannel(this);
        } else if (cirMethod == CirMethod.NONE) {
            //Do nothing
        } else {
            throw new ImException(ImErrorInfo.UNSUPPORTED_CIR_CHANNEL,
                    "Unsupported CIR channel binding");
        }

        if(mCirChannel != null) {
            mCirChannel.connect();
        }
    
voidshutdown()

        shutdownOnError(null);
    
synchronized voidshutdownOnError(com.android.im.engine.ImErrorInfo error)

        if(mState == DISCONNECTED) {
            return;
        }

        if (mCirChannel != null) {
            mCirChannel.shutdown();
        }
        if (mDispatcherThread != null) {
            mDispatcherThread.shutdown();
        }
        if (mDataChannel != null) {
            mDataChannel.shutdown();
        }
        if (mContactListManager != null && !mReestablishing) {
            mContactListManager.reset();
        }
        setState(mReestablishing ? SUSPENDED: DISCONNECTED, error);
        mReestablishing = false;
    
public synchronized voidsuspend()

        setState(SUSPENDING, null);

        if (mCirChannel != null) {
            mCirChannel.shutdown();
        }

        if (mDataChannel != null) {
            mDataChannel.suspend();
        }

        setState(SUSPENDED, null);
    
voidupdateClientInfoAsync(AsyncCompletion completion)

        Primitive updatePresenceRequest = buildUpdatePresenceReq(buildClientInfoElem());

        AsyncTransaction tx = new SimpleAsyncTransaction(mTransactionManager,
                completion);
        tx.sendRequest(updatePresenceRequest);