| 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 | |
| 11 import org.chromium.base.Log; | |
| 12 import org.json.JSONArray; | |
| 13 import org.json.JSONException; | |
| 14 import org.json.JSONObject; | |
| 15 | |
| 16 import java.io.IOException; | |
| 17 import java.net.HttpURLConnection; | |
| 18 import java.net.MalformedURLException; | |
| 19 import java.net.URL; | |
| 20 import java.util.ArrayList; | |
| 21 import java.util.Collections; | |
| 22 import java.util.Comparator; | |
| 23 import java.util.Locale; | |
| 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 private static final String TAG = "Chromoting"; | |
| 43 | |
| 44 /** Path from which to download a user's host list JSON object. */ | |
| 45 private static final String HOST_LIST_PATH = | |
| 46 "https://www.googleapis.com/chromoting/v1/@me/hosts"; | |
| 47 | |
| 48 /** Callback handler to be used for network operations. */ | |
| 49 private Handler mNetworkThread; | |
| 50 | |
| 51 /** Handler for main thread. */ | |
| 52 private Handler mMainThread; | |
| 53 | |
| 54 public HostListLoader() { | |
| 55 // Thread responsible for downloading the host list. | |
| 56 | |
| 57 mMainThread = new Handler(Looper.getMainLooper()); | |
| 58 } | |
| 59 | |
| 60 private void initNetworkThread() { | |
| 61 if (mNetworkThread == null) { | |
| 62 HandlerThread thread = new HandlerThread("network"); | |
| 63 thread.start(); | |
| 64 mNetworkThread = new Handler(thread.getLooper()); | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 /** | |
| 69 * Causes the host list to be fetched on a background thread. This should b
e called on the | |
| 70 * main thread, and callbacks will also be invoked on the main thread. On s
uccess, | |
| 71 * callback.onHostListReceived() will be called, otherwise callback.onError
() will be called | |
| 72 * with an error-code describing the failure. | |
| 73 */ | |
| 74 public void retrieveHostList(String authToken, Callback callback) { | |
| 75 initNetworkThread(); | |
| 76 final String authTokenFinal = authToken; | |
| 77 final Callback callbackFinal = callback; | |
| 78 mNetworkThread.post(new Runnable() { | |
| 79 @Override | |
| 80 public void run() { | |
| 81 doRetrieveHostList(authTokenFinal, callbackFinal); | |
| 82 } | |
| 83 }); | |
| 84 } | |
| 85 | |
| 86 private void doRetrieveHostList(String authToken, Callback callback) { | |
| 87 HttpURLConnection link = null; | |
| 88 String response = null; | |
| 89 try { | |
| 90 link = (HttpURLConnection) new URL(HOST_LIST_PATH).openConnection(); | |
| 91 link.setRequestProperty("Authorization", "OAuth " + authToken); | |
| 92 | |
| 93 // Listen for the server to respond. | |
| 94 int status = link.getResponseCode(); | |
| 95 switch (status) { | |
| 96 case HttpURLConnection.HTTP_OK: // 200 | |
| 97 break; | |
| 98 case HttpURLConnection.HTTP_UNAUTHORIZED: // 401 | |
| 99 postError(callback, Error.AUTH_FAILED); | |
| 100 return; | |
| 101 case HttpURLConnection.HTTP_BAD_GATEWAY: // 502 | |
| 102 case HttpURLConnection.HTTP_UNAVAILABLE: // 503 | |
| 103 postError(callback, Error.SERVICE_UNAVAILABLE); | |
| 104 return; | |
| 105 default: | |
| 106 postError(callback, Error.UNKNOWN); | |
| 107 return; | |
| 108 } | |
| 109 | |
| 110 StringBuilder responseBuilder = new StringBuilder(); | |
| 111 Scanner incoming = new Scanner(link.getInputStream()); | |
| 112 while (incoming.hasNext()) { | |
| 113 responseBuilder.append(incoming.nextLine()); | |
| 114 } | |
| 115 response = String.valueOf(responseBuilder); | |
| 116 incoming.close(); | |
| 117 } catch (MalformedURLException ex) { | |
| 118 // This should never happen. | |
| 119 throw new RuntimeException("Unexpected error while fetching host lis
t: ", ex); | |
| 120 } catch (IOException ex) { | |
| 121 postError(callback, Error.NETWORK_ERROR); | |
| 122 return; | |
| 123 } finally { | |
| 124 if (link != null) { | |
| 125 link.disconnect(); | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 // Parse directory response. | |
| 130 ArrayList<HostInfo> hostList = new ArrayList<HostInfo>(); | |
| 131 try { | |
| 132 JSONObject data = new JSONObject(response).getJSONObject("data"); | |
| 133 if (data.has("items")) { | |
| 134 JSONArray hostsJson = data.getJSONArray("items"); | |
| 135 | |
| 136 int index = 0; | |
| 137 while (!hostsJson.isNull(index)) { | |
| 138 JSONObject hostJson = hostsJson.getJSONObject(index); | |
| 139 // If a host is only recently registered, it may be missing
some of the keys | |
| 140 // below. It should still be visible in the list, even thoug
h a connection | |
| 141 // attempt will fail because of the missing keys. The failed
attempt will | |
| 142 // trigger reloading of the host-list, by which time the key
s will hopefully be | |
| 143 // present, and the retried connection can succeed. | |
| 144 HostInfo host = HostInfo.create(hostJson); | |
| 145 hostList.add(host); | |
| 146 ++index; | |
| 147 } | |
| 148 } | |
| 149 } catch (JSONException ex) { | |
| 150 // Logging the exception stack trace may be too spammy. | |
| 151 Log.e(TAG, "Error parsing host list response: %s", ex.getMessage()); | |
| 152 postError(callback, Error.UNEXPECTED_RESPONSE); | |
| 153 return; | |
| 154 } | |
| 155 | |
| 156 sortHosts(hostList); | |
| 157 | |
| 158 final Callback callbackFinal = callback; | |
| 159 final HostInfo[] hosts = hostList.toArray(new HostInfo[hostList.size()])
; | |
| 160 mMainThread.post(new Runnable() { | |
| 161 @Override | |
| 162 public void run() { | |
| 163 callbackFinal.onHostListReceived(hosts); | |
| 164 } | |
| 165 }); | |
| 166 } | |
| 167 | |
| 168 /** Posts error to callback on main thread. */ | |
| 169 private void postError(Callback callback, Error error) { | |
| 170 final Callback callbackFinal = callback; | |
| 171 final Error errorFinal = error; | |
| 172 mMainThread.post(new Runnable() { | |
| 173 @Override | |
| 174 public void run() { | |
| 175 callbackFinal.onError(errorFinal); | |
| 176 } | |
| 177 }); | |
| 178 } | |
| 179 | |
| 180 private static void sortHosts(ArrayList<HostInfo> hosts) { | |
| 181 Comparator<HostInfo> hostComparator = new Comparator<HostInfo>() { | |
| 182 @Override | |
| 183 public int compare(HostInfo a, HostInfo b) { | |
| 184 if (a.isOnline != b.isOnline) { | |
| 185 return a.isOnline ? -1 : 1; | |
| 186 } | |
| 187 String aName = a.name.toUpperCase(Locale.getDefault()); | |
| 188 String bName = b.name.toUpperCase(Locale.getDefault()); | |
| 189 return aName.compareTo(bName); | |
| 190 } | |
| 191 }; | |
| 192 Collections.sort(hosts, hostComparator); | |
| 193 } | |
| 194 } | |
| OLD | NEW |