Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / remoting / android / java / src / org / chromium / chromoting / HostListLoader.java
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chromoting;
6
7 import android.os.Handler;
8 import android.os.HandlerThread;
9 import android.os.Looper;
10 import android.util.Log;
11
12 import org.chromium.chromoting.jni.JniInterface;
13 import org.json.JSONArray;
14 import org.json.JSONException;
15 import org.json.JSONObject;
16
17 import java.io.IOException;
18 import java.net.HttpURLConnection;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.Scanner;
25
26 /** Helper for fetching the host list. */
27 public class HostListLoader {
28     public enum Error {
29         AUTH_FAILED,
30         NETWORK_ERROR,
31         SERVICE_UNAVAILABLE,
32         UNEXPECTED_RESPONSE,
33         UNKNOWN,
34     }
35
36     /** Callback for receiving the host list, or getting notified of an error. */
37     public interface Callback {
38         void onHostListReceived(HostInfo[] hosts);
39         void onError(Error error);
40     }
41
42     /** Path from which to download a user's host list JSON object. */
43     private static final String HOST_LIST_PATH =
44             "https://www.googleapis.com/chromoting/v1/@me/hosts?key=";
45
46     /** Callback handler to be used for network operations. */
47     private Handler mNetworkThread;
48
49     /** Handler for main thread. */
50     private Handler mMainThread;
51
52     public HostListLoader() {
53         // Thread responsible for downloading the host list.
54
55         mMainThread = new Handler(Looper.getMainLooper());
56     }
57
58     private void initNetworkThread() {
59         if (mNetworkThread == null) {
60             HandlerThread thread = new HandlerThread("network");
61             thread.start();
62             mNetworkThread = new Handler(thread.getLooper());
63         }
64     }
65
66     /**
67       * Causes the host list to be fetched on a background thread. This should be called on the
68       * main thread, and callbacks will also be invoked on the main thread. On success,
69       * callback.onHostListReceived() will be called, otherwise callback.onError() will be called
70       * with an error-code describing the failure.
71       */
72     public void retrieveHostList(String authToken, Callback callback) {
73         initNetworkThread();
74         final String authTokenFinal = authToken;
75         final Callback callbackFinal = callback;
76         mNetworkThread.post(new Runnable() {
77             @Override
78             public void run() {
79                 doRetrieveHostList(authTokenFinal, callbackFinal);
80             }
81         });
82     }
83
84     private void doRetrieveHostList(String authToken, Callback callback) {
85         HttpURLConnection link = null;
86         String response = null;
87         try {
88             link = (HttpURLConnection)
89                     new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()).openConnection();
90             link.addRequestProperty("client_id", JniInterface.nativeGetClientId());
91             link.addRequestProperty("client_secret", JniInterface.nativeGetClientSecret());
92             link.setRequestProperty("Authorization", "OAuth " + authToken);
93
94             // Listen for the server to respond.
95             int status = link.getResponseCode();
96             switch (status) {
97                 case HttpURLConnection.HTTP_OK:  // 200
98                     break;
99                 case HttpURLConnection.HTTP_UNAUTHORIZED:  // 401
100                     postError(callback, Error.AUTH_FAILED);
101                     return;
102                 case HttpURLConnection.HTTP_BAD_GATEWAY:  // 502
103                 case HttpURLConnection.HTTP_UNAVAILABLE:  // 503
104                     postError(callback, Error.SERVICE_UNAVAILABLE);
105                     return;
106                 default:
107                     postError(callback, Error.UNKNOWN);
108                     return;
109             }
110
111             StringBuilder responseBuilder = new StringBuilder();
112             Scanner incoming = new Scanner(link.getInputStream());
113             Log.i("auth", "Successfully authenticated to directory server");
114             while (incoming.hasNext()) {
115                 responseBuilder.append(incoming.nextLine());
116             }
117             response = String.valueOf(responseBuilder);
118             incoming.close();
119         } catch (MalformedURLException ex) {
120             // This should never happen.
121             throw new RuntimeException("Unexpected error while fetching host list: " + ex);
122         } catch (IOException ex) {
123             postError(callback, Error.NETWORK_ERROR);
124             return;
125         } finally {
126             if (link != null) {
127                 link.disconnect();
128             }
129         }
130
131         // Parse directory response.
132         ArrayList<HostInfo> hostList = new ArrayList<HostInfo>();
133         try {
134             JSONObject data = new JSONObject(response).getJSONObject("data");
135             JSONArray hostsJson = data.getJSONArray("items");
136             Log.i("hostlist", "Received host listing from directory server");
137
138             int index = 0;
139             while (!hostsJson.isNull(index)) {
140                 JSONObject hostJson = hostsJson.getJSONObject(index);
141                 // If a host is only recently registered, it may be missing some of the keys below.
142                 // It should still be visible in the list, even though a connection attempt will
143                 // fail because of the missing keys. The failed attempt will trigger reloading of
144                 // the host-list (once crbug.com/304719 is fixed), by which time the keys will
145                 // hopefully be present, and the retried connection can succeed.
146                 HostInfo host = new HostInfo(
147                         hostJson.getString("hostName"),
148                         hostJson.getString("hostId"),
149                         hostJson.optString("jabberId"),
150                         hostJson.optString("publicKey"),
151                         hostJson.optString("status").equals("ONLINE"));
152                 hostList.add(host);
153                 ++index;
154             }
155         } catch (JSONException ex) {
156             postError(callback, Error.UNEXPECTED_RESPONSE);
157             return;
158         }
159
160         sortHosts(hostList);
161
162         final Callback callbackFinal = callback;
163         final HostInfo[] hosts = hostList.toArray(new HostInfo[hostList.size()]);
164         mMainThread.post(new Runnable() {
165             @Override
166             public void run() {
167                 callbackFinal.onHostListReceived(hosts);
168             }
169         });
170     }
171
172     /** Posts error to callback on main thread. */
173     private void postError(Callback callback, Error error) {
174         final Callback callbackFinal = callback;
175         final Error errorFinal = error;
176         mMainThread.post(new Runnable() {
177             @Override
178             public void run() {
179                 callbackFinal.onError(errorFinal);
180             }
181         });
182     }
183
184     private static void sortHosts(ArrayList<HostInfo> hosts) {
185         Comparator<HostInfo> hostComparator = new Comparator<HostInfo>() {
186             public int compare(HostInfo a, HostInfo b) {
187                 if (a.isOnline != b.isOnline) {
188                     return a.isOnline ? -1 : 1;
189                 }
190                 String aName = a.name.toUpperCase();
191                 String bName = b.name.toUpperCase();
192                 return aName.compareTo(bName);
193             }
194         };
195         Collections.sort(hosts, hostComparator);
196     }
197 }