Home | History | Annotate | Download | only in imps
      1 /*
      2  * Copyright (C) 2007 Esmertec AG.
      3  * Copyright (C) 2007 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package com.android.im.imps;
     19 
     20 import java.util.ArrayList;
     21 import java.util.HashMap;
     22 import java.util.List;
     23 
     24 import com.android.im.engine.Contact;
     25 import com.android.im.engine.ImErrorInfo;
     26 import com.android.im.engine.ImException;
     27 import com.android.im.engine.LoginInfo;
     28 import com.android.im.imps.ImpsConnectionConfig.CirMethod;
     29 import com.android.im.imps.ImpsConnectionConfig.TransportType;
     30 
     31 /**
     32  * Represents the context of an IMPS session. The IMPS session is a framework in
     33  * which the IMPS services are provided to the IMPS client. It's established
     34  * when the client logs in and terminated when either the client logs out or the
     35  * SAP decides to disconnect the session.
     36  */
     37 public class ImpsSession {
     38     private static final String KEY_CIR_HTTP_ADDRESS = "cirHttpAddress";
     39     private static final String KEY_CIR_TCP_PORT = "cirTcpPort";
     40     private static final String KEY_CIR_TCP_ADDRESS = "cirTcpAddress";
     41     private static final String KEY_CIR_METHOD = "CirMethod";
     42     private static final String KEY_SERVER_POLL_MIN = "serverPollMin";
     43     private static final String KEY_PASSWORD = "password";
     44     private static final String KEY_USERNAME = "username";
     45     private static final String KEY_KEEP_ALIVE_TIME = "keepAliveTime";
     46     private static final String KEY_SESSION_COOKIE = "sessionCookie";
     47     private static final String KEY_SESSION_ID = "sessionId";
     48 
     49     private static final int DEFAULT_TCP_PORT = 3171;
     50 
     51     private ImpsConnection mConnection;
     52     private String mId;
     53     private String mCookie;
     54     private long mKeepAliveTime;
     55     private CirMethod mCurrentCirMethod;
     56     private String mCirTcpAddress;
     57     private int mCirTcpPort = DEFAULT_TCP_PORT;
     58     private long mServerPollMin;
     59     private String mCirHttpAddress;
     60     private LoginInfo mLoginInfo;
     61 
     62     private boolean mCapablityRequest;
     63     private List<CirMethod> mSupportedCirMethod;
     64 
     65     private Contact mLoginUser;
     66 
     67     PrimitiveElement mServiceTree;
     68 
     69     /**
     70      * Flag that indicates this is a new created session or not.
     71      */
     72     private boolean mNew;
     73 
     74     ImpsSession(ImpsConnection connection, LoginInfo info) throws ImException{
     75         mConnection = connection;
     76         setLoginInfo(info);
     77 
     78         mNew = true;
     79         mCookie = ImpsUtils.genSessionCookie();
     80 
     81         mCirTcpPort = DEFAULT_TCP_PORT;
     82         mServerPollMin = connection.getConfig().getDefaultServerPollMin();
     83     }
     84 
     85     ImpsSession(ImpsConnection connection, HashMap<String, String> values)
     86             throws ImException {
     87         mConnection = connection;
     88         mNew = false;
     89         mId = values.get(KEY_SESSION_ID);
     90         if (mId == null || mId.length() == 0) {
     91             throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
     92                 "Missing session id");
     93         }
     94         mCookie = values.get(KEY_SESSION_COOKIE);
     95         if (mCookie == null || mCookie.length() == 0) {
     96             throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
     97                 "Missing session cookie");
     98         }
     99         try {
    100             mKeepAliveTime = Long.parseLong(values.get(KEY_KEEP_ALIVE_TIME));
    101         } catch (NumberFormatException e) {
    102             throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
    103                 "Invalid keepAliveTime");
    104         }
    105         try {
    106             mServerPollMin = Long.parseLong(values.get(KEY_SERVER_POLL_MIN));
    107         } catch (NumberFormatException e) {
    108             throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
    109                 "Invalid serverPollMin");
    110         }
    111         String username = values.get(KEY_USERNAME);
    112         String password = values.get(KEY_PASSWORD);
    113         // Empty password might be valid
    114         if (username == null || username.length() == 0 || password == null) {
    115             throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
    116                 "Invalid username or password");
    117         }
    118         setLoginInfo(new LoginInfo(username, password));
    119 
    120         mCurrentCirMethod = CirMethod.valueOf(values.get(KEY_CIR_METHOD));
    121         if (mCurrentCirMethod == CirMethod.STCP) {
    122             mCirTcpAddress = values.get(KEY_CIR_TCP_ADDRESS);
    123             if (mCirTcpAddress == null || mCirTcpAddress.length() == 0) {
    124                 throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
    125                     "Missing CirTcpAddress");
    126             }
    127             try {
    128                 mCirTcpPort = Integer.parseInt(values.get(KEY_CIR_TCP_PORT));
    129             } catch (NumberFormatException e) {
    130                 throw new ImException(ImErrorInfo.INVALID_SESSION_CONTEXT,
    131                     "Invalid CirTcpPort");
    132             }
    133         } else if (mCurrentCirMethod == CirMethod.SHTTP) {
    134             mCirHttpAddress = values.get(KEY_CIR_HTTP_ADDRESS);
    135         }
    136     }
    137 
    138     public HashMap<String, String> getContext() {
    139         HashMap<String, String> values = new HashMap<String, String>();
    140 
    141         values.put(KEY_SESSION_ID, mId);
    142         values.put(KEY_SESSION_COOKIE, mCookie);
    143         values.put(KEY_KEEP_ALIVE_TIME, Long.toString(mKeepAliveTime));
    144         values.put(KEY_USERNAME, mLoginInfo.getUserName());
    145         values.put(KEY_PASSWORD, mLoginInfo.getPassword());
    146         values.put(KEY_SERVER_POLL_MIN, Long.toString(mServerPollMin));
    147 
    148         values.put(KEY_CIR_METHOD, mCurrentCirMethod.name());
    149         if(mCurrentCirMethod == CirMethod.STCP) {
    150             values.put(KEY_CIR_TCP_ADDRESS, mCirTcpAddress);
    151             values.put(KEY_CIR_TCP_PORT, Integer.toString(mCirTcpPort));
    152         } else if (mCurrentCirMethod == CirMethod.SHTTP) {
    153             values.put(KEY_CIR_HTTP_ADDRESS, mCirHttpAddress);
    154         }
    155         return values;
    156     }
    157 
    158     /**
    159      * Gets the unique id of the session.
    160      *
    161      * @return the unique id of the session.
    162      */
    163     public String getID() {
    164         return mId;
    165     }
    166 
    167     public void setId(String id) {
    168         mId = id;
    169     }
    170 
    171     public String getCookie() {
    172         return mCookie;
    173     }
    174 
    175     public String getUserName() {
    176         return mLoginInfo.getUserName();
    177     }
    178 
    179     public String getPassword() {
    180         return mLoginInfo.getPassword();
    181     }
    182 
    183     public LoginInfo getLoginInfo() {
    184         return mLoginInfo;
    185     }
    186     /**
    187      * Gets the auto logout timer value.
    188      *
    189      * @return the auto logout timer value.
    190      */
    191     public long getKeepAliveTime() {
    192         return mKeepAliveTime;
    193     }
    194 
    195     public void setKeepAliveTime(long keepAliveTime) {
    196         mKeepAliveTime = keepAliveTime;
    197     }
    198 
    199     /**
    200      * Gets if further capability request is required in the session.
    201      *
    202      * @return <code>true</code> if further capability request is required.
    203      */
    204     public boolean isCapablityRequestRequired() {
    205         return mCapablityRequest || mNew;
    206     }
    207 
    208     public void setCapablityRequestRequired(boolean required) {
    209         mCapablityRequest = required;
    210     }
    211 
    212     public ImpsUserAddress getLoginUserAddress() {
    213         return (ImpsUserAddress) mLoginUser.getAddress();
    214     }
    215 
    216     public Contact getLoginUser() {
    217         return mLoginUser;
    218     }
    219 
    220     /**
    221      * Sets the Login information. After login successfully, the login
    222      * information should be saved in the session context so that we can auto
    223      * login when reconnect to the server.
    224      *
    225      * @param loginInfo the login information.
    226      * @throws ImException
    227      */
    228     private void setLoginInfo(LoginInfo loginInfo) throws ImException {
    229         try {
    230             ImpsAddress address = new ImpsUserAddress(loginInfo.getUserName());
    231             mLoginUser = new Contact(address, address.getScreenName());
    232             mLoginInfo = loginInfo;
    233         } catch (IllegalArgumentException e) {
    234             throw new ImException(ImErrorInfo.INVALID_USERNAME,
    235                     "Invalid username");
    236         }
    237     }
    238 
    239     /**
    240      * Gets a collection of CIR methods that are supported by both the client
    241      * and the server.
    242      *
    243      * @return a collection of supported CIR methods
    244      */
    245     public List<CirMethod> getSupportedCirMethods() {
    246         return mSupportedCirMethod;
    247     }
    248 
    249     public CirMethod getCurrentCirMethod() {
    250         return mCurrentCirMethod;
    251     }
    252 
    253     public void setCurrentCirMethod(CirMethod cirMethod) {
    254         mCurrentCirMethod = cirMethod;
    255     }
    256 
    257     /**
    258      * Gets the IP address for standalone TCP/IP CIR method.
    259      *
    260      * @return the IP address for standalone TCP/IP CIR method
    261      */
    262     public String getCirTcpAddress() {
    263         return mCirTcpAddress;
    264     }
    265 
    266     /**
    267      * Gets the port number for the standalone TCP/IP CIR method.
    268      *
    269      * @return the port number for the standalone TCP/IP CIR method.
    270      */
    271     public int getCirTcpPort() {
    272         return mCirTcpPort;
    273     }
    274 
    275     /**
    276      * Gets the minimum time interval (in seconds) that MUST pass before two
    277      * subsequent PollingRequest transactions.
    278      *
    279      * @return the minimum time interval in seconds.
    280      */
    281     public long getServerPollMin() {
    282         return mServerPollMin;
    283     }
    284 
    285     /**
    286      * Gets the URL used for standalone HTTP binding of CIR channel.
    287      *
    288      * @return the URL.
    289      */
    290     public String getCirHttpAddress() {
    291         return mCirHttpAddress;
    292     }
    293 
    294     /**
    295      * Gets the service tree of the features and functions that the server
    296      * supports.
    297      *
    298      * @return the service tree.
    299      */
    300     public PrimitiveElement getServiceTree() {
    301         return mServiceTree;
    302     }
    303 
    304     /**
    305      * Perform client capability negotiation with the server asynchronously.
    306      *
    307      * @param completion Async completion object.
    308      */
    309     public void negotiateCapabilityAsync(AsyncCompletion completion) {
    310         Primitive capabilityRequest = buildCapabilityRequest();
    311 
    312         AsyncTransaction tx = new AsyncTransaction(
    313                 mConnection.getTransactionManager(), completion) {
    314 
    315             @Override
    316             public void onResponseOk(Primitive response) {
    317                 extractCapability(response);
    318             }
    319 
    320             @Override
    321             public void onResponseError(ImpsErrorInfo error) { }
    322         };
    323 
    324         tx.sendRequest(capabilityRequest);
    325     }
    326 
    327     /**
    328      * Perform service negotiation with the server asynchronously.
    329      *
    330      * @param completion Async completion object.
    331      */
    332     public void negotiateServiceAsync(AsyncCompletion completion) {
    333         Primitive serviceRequest = buildServiceRequest();
    334         AsyncTransaction tx = new AsyncTransaction(
    335                 mConnection.getTransactionManager(), completion) {
    336 
    337             @Override
    338             public void onResponseOk(Primitive response) {
    339                 mServiceTree = response.getElement(ImpsTags.AllFunctions).getFirstChild();
    340             }
    341 
    342             @Override
    343             public void onResponseError(ImpsErrorInfo error) { }
    344         };
    345 
    346         tx.sendRequest(serviceRequest);
    347     }
    348 
    349     private Primitive buildCapabilityRequest() {
    350         Primitive capabilityRequest = new Primitive(ImpsTags.ClientCapability_Request);
    351         PrimitiveElement list = capabilityRequest.addElement(ImpsTags.CapabilityList);
    352         list.addChild(ImpsTags.ClientType, ImpsClientCapability.getClientType());
    353         list.addChild(ImpsTags.AcceptedContentLength, Integer
    354                 .toString(ImpsClientCapability.getAcceptedContentLength()));
    355         list.addChild(ImpsTags.ParserSize,
    356                 Integer.toString(ImpsClientCapability.getParserSize()));
    357         list.addChild(ImpsTags.MultiTrans,
    358                 Integer.toString(ImpsClientCapability.getMultiTrans()));
    359 
    360         // TODO: MultiTransPerMessage is IMPS 1.3
    361         //list.addChild(ImpsTags.MultiTransPerMessage,
    362         //        Integer.toString(ImpsClientCapability.getMultiTransPerMessage()));
    363         list.addChild(ImpsTags.InitialDeliveryMethod,
    364                 ImpsClientCapability.getInitialDeliveryMethod());
    365         list.addChild(ImpsTags.ServerPollMin, Long.toString(mServerPollMin));
    366 
    367         for(TransportType supportedBear : ImpsClientCapability.getSupportedBearers()) {
    368             list.addChild(ImpsTags.SupportedBearer, supportedBear.toString());
    369         }
    370 
    371         for(CirMethod supportedCirMethod : ImpsClientCapability.getSupportedCirMethods()) {
    372             list.addChild(ImpsTags.SupportedCIRMethod, supportedCirMethod.toString());
    373             if (CirMethod.SUDP.equals(supportedCirMethod)) {
    374                 list.addChild(ImpsTags.UDPPort,
    375                         Integer.toString(mConnection.getConfig().getUdpPort()));
    376             }
    377         }
    378 
    379         return capabilityRequest;
    380     }
    381 
    382     /* keep this method package private instead of private to avoid the
    383      * overhead of calling from a inner class.
    384      */
    385     void extractCapability(Primitive capabilityResponse) {
    386         mSupportedCirMethod = new ArrayList<CirMethod>();
    387 
    388         PrimitiveElement agreedList = capabilityResponse.getContentElement()
    389                 .getFirstChild();
    390         for (PrimitiveElement element : agreedList.getChildren()) {
    391             String tag = element.getTagName();
    392             if (tag.equals(ImpsTags.SupportedCIRMethod)) {
    393                 try {
    394                     mSupportedCirMethod.add(CirMethod.valueOf(element.getContents()));
    395                 } catch (IllegalArgumentException e) {
    396                     ImpsLog.log("Unrecognized CIR method " + element.getContents());
    397                 }
    398             } else if (tag.equals(ImpsTags.TCPAddress)) {
    399                 mCirTcpAddress = element.getContents();
    400             } else if (tag.equals(ImpsTags.TCPPort)) {
    401                 mCirTcpPort = (int)ImpsUtils.parseLong(element.getContents(),
    402                         DEFAULT_TCP_PORT);
    403             } else if (tag.equals(ImpsTags.ServerPollMin)) {
    404                 long defaultPollMin = mConnection.getConfig().getDefaultServerPollMin();
    405                 mServerPollMin = ImpsUtils.parseLong(element.getContents(),
    406                         defaultPollMin);
    407                 if (mServerPollMin <= 0) {
    408                     mServerPollMin = defaultPollMin;
    409                 }
    410             } else if (tag.equals(ImpsTags.CIRHTTPAddress)
    411                     || tag.equals(ImpsTags.CIRURL)) {
    412                 // This tag is CIRHTTPAddress in 1.3 and CIRURL in 1.2
    413                 mCirHttpAddress = element.getChildContents(ImpsTags.URL);
    414             }
    415         }
    416     }
    417 
    418     private Primitive buildServiceRequest() {
    419         Primitive serviceRequest = new Primitive(ImpsTags.Service_Request);
    420         PrimitiveElement functions = serviceRequest.addElement(ImpsTags.Functions);
    421         PrimitiveElement features = functions.addChild(ImpsTags.WVCSPFeat);
    422         features.addChild(ImpsTags.FundamentalFeat);
    423         features.addChild(ImpsTags.PresenceFeat);
    424         features.addChild(ImpsTags.IMFeat);
    425         features.addChild(ImpsTags.GroupFeat);
    426         serviceRequest.addElement(ImpsTags.AllFunctionsRequest, true);
    427         return serviceRequest;
    428     }
    429 }
    430