• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.example.android.samplesync.client;
18 
19 import android.accounts.Account;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.util.Log;
23 
24 import com.example.android.samplesync.authenticator.AuthenticatorActivity;
25 
26 import org.apache.http.HttpEntity;
27 import org.apache.http.HttpResponse;
28 import org.apache.http.HttpStatus;
29 import org.apache.http.NameValuePair;
30 import org.apache.http.ParseException;
31 import org.apache.http.auth.AuthenticationException;
32 import org.apache.http.client.HttpClient;
33 import org.apache.http.client.entity.UrlEncodedFormEntity;
34 import org.apache.http.client.methods.HttpPost;
35 import org.apache.http.conn.params.ConnManagerParams;
36 import org.apache.http.impl.client.DefaultHttpClient;
37 import org.apache.http.message.BasicNameValuePair;
38 import org.apache.http.params.HttpConnectionParams;
39 import org.apache.http.params.HttpParams;
40 import org.apache.http.util.EntityUtils;
41 import org.json.JSONArray;
42 import org.json.JSONException;
43 
44 import java.io.IOException;
45 import java.io.UnsupportedEncodingException;
46 import java.text.SimpleDateFormat;
47 import java.util.ArrayList;
48 import java.util.Date;
49 import java.util.List;
50 import java.util.TimeZone;
51 
52 /**
53  * Provides utility methods for communicating with the server.
54  */
55 public class NetworkUtilities {
56     private static final String TAG = "NetworkUtilities";
57     public static final String PARAM_USERNAME = "username";
58     public static final String PARAM_PASSWORD = "password";
59     public static final String PARAM_UPDATED = "timestamp";
60     public static final String USER_AGENT = "AuthenticationService/1.0";
61     public static final int REGISTRATION_TIMEOUT = 30 * 1000; // ms
62     public static final String BASE_URL =
63         "https://samplesyncadapter.appspot.com";
64     public static final String AUTH_URI = BASE_URL + "/auth";
65     public static final String FETCH_FRIEND_UPDATES_URI =
66         BASE_URL + "/fetch_friend_updates";
67     public static final String FETCH_STATUS_URI = BASE_URL + "/fetch_status";
68     private static HttpClient mHttpClient;
69 
70     /**
71      * Configures the httpClient to connect to the URL provided.
72      */
maybeCreateHttpClient()73     public static void maybeCreateHttpClient() {
74         if (mHttpClient == null) {
75             mHttpClient = new DefaultHttpClient();
76             final HttpParams params = mHttpClient.getParams();
77             HttpConnectionParams.setConnectionTimeout(params,
78                 REGISTRATION_TIMEOUT);
79             HttpConnectionParams.setSoTimeout(params, REGISTRATION_TIMEOUT);
80             ConnManagerParams.setTimeout(params, REGISTRATION_TIMEOUT);
81         }
82     }
83 
84     /**
85      * Executes the network requests on a separate thread.
86      *
87      * @param runnable The runnable instance containing network mOperations to
88      *        be executed.
89      */
performOnBackgroundThread(final Runnable runnable)90     public static Thread performOnBackgroundThread(final Runnable runnable) {
91         final Thread t = new Thread() {
92             @Override
93             public void run() {
94                 try {
95                     runnable.run();
96                 } finally {
97 
98                 }
99             }
100         };
101         t.start();
102         return t;
103     }
104 
105     /**
106      * Connects to the Voiper server, authenticates the provided username and
107      * password.
108      *
109      * @param username The user's username
110      * @param password The user's password
111      * @param handler The hander instance from the calling UI thread.
112      * @param context The context of the calling Activity.
113      * @return boolean The boolean result indicating whether the user was
114      *         successfully authenticated.
115      */
authenticate(String username, String password, Handler handler, final Context context)116     public static boolean authenticate(String username, String password,
117         Handler handler, final Context context) {
118         final HttpResponse resp;
119 
120         final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
121         params.add(new BasicNameValuePair(PARAM_USERNAME, username));
122         params.add(new BasicNameValuePair(PARAM_PASSWORD, password));
123         HttpEntity entity = null;
124         try {
125             entity = new UrlEncodedFormEntity(params);
126         } catch (final UnsupportedEncodingException e) {
127             // this should never happen.
128             throw new AssertionError(e);
129         }
130         final HttpPost post = new HttpPost(AUTH_URI);
131         post.addHeader(entity.getContentType());
132         post.setEntity(entity);
133         maybeCreateHttpClient();
134 
135         try {
136             resp = mHttpClient.execute(post);
137             if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
138                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
139                     Log.v(TAG, "Successful authentication");
140                 }
141                 sendResult(true, handler, context);
142                 return true;
143             } else {
144                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
145                     Log.v(TAG, "Error authenticating" + resp.getStatusLine());
146                 }
147                 sendResult(false, handler, context);
148                 return false;
149             }
150         } catch (final IOException e) {
151             if (Log.isLoggable(TAG, Log.VERBOSE)) {
152                 Log.v(TAG, "IOException when getting authtoken", e);
153             }
154             sendResult(false, handler, context);
155             return false;
156         } finally {
157             if (Log.isLoggable(TAG, Log.VERBOSE)) {
158                 Log.v(TAG, "getAuthtoken completing");
159             }
160         }
161     }
162 
163     /**
164      * Sends the authentication response from server back to the caller main UI
165      * thread through its handler.
166      *
167      * @param result The boolean holding authentication result
168      * @param handler The main UI thread's handler instance.
169      * @param context The caller Activity's context.
170      */
sendResult(final Boolean result, final Handler handler, final Context context)171     private static void sendResult(final Boolean result, final Handler handler,
172         final Context context) {
173         if (handler == null || context == null) {
174             return;
175         }
176         handler.post(new Runnable() {
177             public void run() {
178                 ((AuthenticatorActivity) context).onAuthenticationResult(result);
179             }
180         });
181     }
182 
183     /**
184      * Attempts to authenticate the user credentials on the server.
185      *
186      * @param username The user's username
187      * @param password The user's password to be authenticated
188      * @param handler The main UI thread's handler instance.
189      * @param context The caller Activity's context
190      * @return Thread The thread on which the network mOperations are executed.
191      */
attemptAuth(final String username, final String password, final Handler handler, final Context context)192     public static Thread attemptAuth(final String username,
193         final String password, final Handler handler, final Context context) {
194         final Runnable runnable = new Runnable() {
195             public void run() {
196                 authenticate(username, password, handler, context);
197             }
198         };
199         // run on background thread.
200         return NetworkUtilities.performOnBackgroundThread(runnable);
201     }
202 
203     /**
204      * Fetches the list of friend data updates from the server
205      *
206      * @param account The account being synced.
207      * @param authtoken The authtoken stored in AccountManager for this account
208      * @param lastUpdated The last time that sync was performed
209      * @return list The list of updates received from the server.
210      */
fetchFriendUpdates(Account account, String authtoken, Date lastUpdated)211     public static List<User> fetchFriendUpdates(Account account,
212         String authtoken, Date lastUpdated) throws JSONException,
213         ParseException, IOException, AuthenticationException {
214         final ArrayList<User> friendList = new ArrayList<User>();
215         final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
216         params.add(new BasicNameValuePair(PARAM_USERNAME, account.name));
217         params.add(new BasicNameValuePair(PARAM_PASSWORD, authtoken));
218         if (lastUpdated != null) {
219             final SimpleDateFormat formatter =
220                 new SimpleDateFormat("yyyy/MM/dd HH:mm");
221             formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
222             params.add(new BasicNameValuePair(PARAM_UPDATED, formatter
223                 .format(lastUpdated)));
224         }
225         Log.i(TAG, params.toString());
226 
227         HttpEntity entity = null;
228         entity = new UrlEncodedFormEntity(params);
229         final HttpPost post = new HttpPost(FETCH_FRIEND_UPDATES_URI);
230         post.addHeader(entity.getContentType());
231         post.setEntity(entity);
232         maybeCreateHttpClient();
233 
234         final HttpResponse resp = mHttpClient.execute(post);
235         final String response = EntityUtils.toString(resp.getEntity());
236 
237         if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
238             // Succesfully connected to the samplesyncadapter server and
239             // authenticated.
240             // Extract friends data in json format.
241             final JSONArray friends = new JSONArray(response);
242             Log.d(TAG, response);
243             for (int i = 0; i < friends.length(); i++) {
244                 friendList.add(User.valueOf(friends.getJSONObject(i)));
245             }
246         } else {
247             if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
248                 Log.e(TAG,
249                     "Authentication exception in fetching remote contacts");
250                 throw new AuthenticationException();
251             } else {
252                 Log.e(TAG, "Server error in fetching remote contacts: "
253                     + resp.getStatusLine());
254                 throw new IOException();
255             }
256         }
257         return friendList;
258     }
259 
260     /**
261      * Fetches status messages for the user's friends from the server
262      *
263      * @param account The account being synced.
264      * @param authtoken The authtoken stored in the AccountManager for the
265      *        account
266      * @return list The list of status messages received from the server.
267      */
fetchFriendStatuses(Account account, String authtoken)268     public static List<User.Status> fetchFriendStatuses(Account account,
269         String authtoken) throws JSONException, ParseException, IOException,
270         AuthenticationException {
271         final ArrayList<User.Status> statusList = new ArrayList<User.Status>();
272         final ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();
273         params.add(new BasicNameValuePair(PARAM_USERNAME, account.name));
274         params.add(new BasicNameValuePair(PARAM_PASSWORD, authtoken));
275 
276         HttpEntity entity = null;
277         entity = new UrlEncodedFormEntity(params);
278         final HttpPost post = new HttpPost(FETCH_STATUS_URI);
279         post.addHeader(entity.getContentType());
280         post.setEntity(entity);
281         maybeCreateHttpClient();
282 
283         final HttpResponse resp = mHttpClient.execute(post);
284         final String response = EntityUtils.toString(resp.getEntity());
285 
286         if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
287             // Succesfully connected to the samplesyncadapter server and
288             // authenticated.
289             // Extract friends data in json format.
290             final JSONArray statuses = new JSONArray(response);
291             for (int i = 0; i < statuses.length(); i++) {
292                 statusList.add(User.Status.valueOf(statuses.getJSONObject(i)));
293             }
294         } else {
295             if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
296                 Log.e(TAG,
297                     "Authentication exception in fetching friend status list");
298                 throw new AuthenticationException();
299             } else {
300                 Log.e(TAG, "Server error in fetching friend status list");
301                 throw new IOException();
302             }
303         }
304         return statusList;
305     }
306 
307 }
308