Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / remoting / android / java / src / org / chromium / chromoting / HostListLoader.java
blobbfcdfd8adac22dac6e0870222cb4a277ae9d73e8
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.
5 package org.chromium.chromoting;
7 import android.os.Handler;
8 import android.os.HandlerThread;
9 import android.os.Looper;
10 import android.util.Log;
12 import org.chromium.chromoting.jni.JniInterface;
13 import org.json.JSONArray;
14 import org.json.JSONException;
15 import org.json.JSONObject;
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.Locale;
25 import java.util.Scanner;
27 /** Helper for fetching the host list. */
28 public class HostListLoader {
29 public enum Error {
30 AUTH_FAILED,
31 NETWORK_ERROR,
32 SERVICE_UNAVAILABLE,
33 UNEXPECTED_RESPONSE,
34 UNKNOWN,
37 /** Callback for receiving the host list, or getting notified of an error. */
38 public interface Callback {
39 void onHostListReceived(HostInfo[] hosts);
40 void onError(Error error);
43 /** Path from which to download a user's host list JSON object. */
44 private static final String HOST_LIST_PATH =
45 "https://www.googleapis.com/chromoting/v1/@me/hosts?key=";
47 /** Callback handler to be used for network operations. */
48 private Handler mNetworkThread;
50 /** Handler for main thread. */
51 private Handler mMainThread;
53 public HostListLoader() {
54 // Thread responsible for downloading the host list.
56 mMainThread = new Handler(Looper.getMainLooper());
59 private void initNetworkThread() {
60 if (mNetworkThread == null) {
61 HandlerThread thread = new HandlerThread("network");
62 thread.start();
63 mNetworkThread = new Handler(thread.getLooper());
67 /**
68 * Causes the host list to be fetched on a background thread. This should be called on the
69 * main thread, and callbacks will also be invoked on the main thread. On success,
70 * callback.onHostListReceived() will be called, otherwise callback.onError() will be called
71 * with an error-code describing the failure.
73 public void retrieveHostList(String authToken, Callback callback) {
74 initNetworkThread();
75 final String authTokenFinal = authToken;
76 final Callback callbackFinal = callback;
77 mNetworkThread.post(new Runnable() {
78 @Override
79 public void run() {
80 doRetrieveHostList(authTokenFinal, callbackFinal);
82 });
85 private void doRetrieveHostList(String authToken, Callback callback) {
86 HttpURLConnection link = null;
87 String response = null;
88 try {
89 link = (HttpURLConnection)
90 new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()).openConnection();
91 link.addRequestProperty("client_id", JniInterface.nativeGetClientId());
92 link.addRequestProperty("client_secret", JniInterface.nativeGetClientSecret());
93 link.setRequestProperty("Authorization", "OAuth " + authToken);
95 // Listen for the server to respond.
96 int status = link.getResponseCode();
97 switch (status) {
98 case HttpURLConnection.HTTP_OK: // 200
99 break;
100 case HttpURLConnection.HTTP_UNAUTHORIZED: // 401
101 postError(callback, Error.AUTH_FAILED);
102 return;
103 case HttpURLConnection.HTTP_BAD_GATEWAY: // 502
104 case HttpURLConnection.HTTP_UNAVAILABLE: // 503
105 postError(callback, Error.SERVICE_UNAVAILABLE);
106 return;
107 default:
108 postError(callback, Error.UNKNOWN);
109 return;
112 StringBuilder responseBuilder = new StringBuilder();
113 Scanner incoming = new Scanner(link.getInputStream());
114 Log.i("auth", "Successfully authenticated to directory server");
115 while (incoming.hasNext()) {
116 responseBuilder.append(incoming.nextLine());
118 response = String.valueOf(responseBuilder);
119 incoming.close();
120 } catch (MalformedURLException ex) {
121 // This should never happen.
122 throw new RuntimeException("Unexpected error while fetching host list: " + ex);
123 } catch (IOException ex) {
124 postError(callback, Error.NETWORK_ERROR);
125 return;
126 } finally {
127 if (link != null) {
128 link.disconnect();
132 // Parse directory response.
133 ArrayList<HostInfo> hostList = new ArrayList<HostInfo>();
134 try {
135 JSONObject data = new JSONObject(response).getJSONObject("data");
136 if (data.has("items")) {
137 JSONArray hostsJson = data.getJSONArray("items");
138 Log.i("hostlist", "Received host listing from directory server");
140 int index = 0;
141 while (!hostsJson.isNull(index)) {
142 JSONObject hostJson = hostsJson.getJSONObject(index);
143 // If a host is only recently registered, it may be missing some of the keys
144 // below. It should still be visible in the list, even though a connection
145 // attempt will fail because of the missing keys. The failed attempt will
146 // trigger reloading of the host-list, by which time the keys will hopefully be
147 // present, and the retried connection can succeed.
148 HostInfo host = HostInfo.create(hostJson);
149 hostList.add(host);
150 ++index;
153 } catch (JSONException ex) {
154 Log.e("hostlist", "Error parsing host list response: ", ex);
155 postError(callback, Error.UNEXPECTED_RESPONSE);
156 return;
159 sortHosts(hostList);
161 final Callback callbackFinal = callback;
162 final HostInfo[] hosts = hostList.toArray(new HostInfo[hostList.size()]);
163 mMainThread.post(new Runnable() {
164 @Override
165 public void run() {
166 callbackFinal.onHostListReceived(hosts);
171 /** Posts error to callback on main thread. */
172 private void postError(Callback callback, Error error) {
173 final Callback callbackFinal = callback;
174 final Error errorFinal = error;
175 mMainThread.post(new Runnable() {
176 @Override
177 public void run() {
178 callbackFinal.onError(errorFinal);
183 private static void sortHosts(ArrayList<HostInfo> hosts) {
184 Comparator<HostInfo> hostComparator = new Comparator<HostInfo>() {
185 @Override
186 public int compare(HostInfo a, HostInfo b) {
187 if (a.isOnline != b.isOnline) {
188 return a.isOnline ? -1 : 1;
190 String aName = a.name.toUpperCase(Locale.getDefault());
191 String bName = b.name.toUpperCase(Locale.getDefault());
192 return aName.compareTo(bName);
195 Collections.sort(hosts, hostComparator);