• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ImpsSession(ImpsConnection connection, LoginInfo info)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 
ImpsSession(ImpsConnection connection, HashMap<String, String> values)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 
getContext()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      */
getID()163     public String getID() {
164         return mId;
165     }
166 
setId(String id)167     public void setId(String id) {
168         mId = id;
169     }
170 
getCookie()171     public String getCookie() {
172         return mCookie;
173     }
174 
getUserName()175     public String getUserName() {
176         return mLoginInfo.getUserName();
177     }
178 
getPassword()179     public String getPassword() {
180         return mLoginInfo.getPassword();
181     }
182 
getLoginInfo()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      */
getKeepAliveTime()191     public long getKeepAliveTime() {
192         return mKeepAliveTime;
193     }
194 
setKeepAliveTime(long keepAliveTime)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      */
isCapablityRequestRequired()204     public boolean isCapablityRequestRequired() {
205         return mCapablityRequest || mNew;
206     }
207 
setCapablityRequestRequired(boolean required)208     public void setCapablityRequestRequired(boolean required) {
209         mCapablityRequest = required;
210     }
211 
getLoginUserAddress()212     public ImpsUserAddress getLoginUserAddress() {
213         return (ImpsUserAddress) mLoginUser.getAddress();
214     }
215 
getLoginUser()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      */
setLoginInfo(LoginInfo loginInfo)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      */
getSupportedCirMethods()245     public List<CirMethod> getSupportedCirMethods() {
246         return mSupportedCirMethod;
247     }
248 
getCurrentCirMethod()249     public CirMethod getCurrentCirMethod() {
250         return mCurrentCirMethod;
251     }
252 
setCurrentCirMethod(CirMethod cirMethod)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      */
getCirTcpAddress()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      */
getCirTcpPort()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      */
getServerPollMin()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      */
getCirHttpAddress()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      */
getServiceTree()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      */
negotiateCapabilityAsync(AsyncCompletion completion)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      */
negotiateServiceAsync(AsyncCompletion completion)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 
buildCapabilityRequest()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      */
extractCapability(Primitive capabilityResponse)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 
buildServiceRequest()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