OLD | NEW |
(Empty) | |
| 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 b
e called on the |
| 68 * main thread, and callbacks will also be invoked on the main thread. On s
uccess, |
| 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()).ope
nConnection(); |
| 90 link.addRequestProperty("client_id", JniInterface.nativeGetClientId(
)); |
| 91 link.addRequestProperty("client_secret", JniInterface.nativeGetClien
tSecret()); |
| 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 lis
t: " + 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 connect
ion attempt will |
| 143 // fail because of the missing keys. The failed attempt will tri
gger 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 } |
OLD | NEW |