1d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/* 2d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Copyright (C) 2017 The Android Open Source Project 3d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 4d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * you may not use this file except in compliance with the License. 6d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * You may obtain a copy of the License at 7d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 8d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 10d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * See the License for the specific language governing permissions and 14d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * limitations under the License 15d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 16d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 17d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpackage com.android.incallui.calllocation.impl; 18d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 19d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport static com.android.dialer.util.DialerUtils.closeQuietly; 20d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 21d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.content.Context; 22d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.net.Uri; 23d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.net.Uri.Builder; 24d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.os.SystemClock; 25d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport android.util.Pair; 26d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.dialer.common.LogUtil; 27d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.dialer.util.MoreStrings; 28d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.google.android.common.http.UrlRules; 29d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.ByteArrayOutputStream; 30d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.FilterInputStream; 31d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.IOException; 32d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.io.InputStream; 33d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.HttpURLConnection; 34d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.MalformedURLException; 35d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.ProtocolException; 36d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.net.URL; 37d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.List; 38d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.Objects; 39d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport java.util.Set; 40d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 41d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian/** Utility for making http requests. */ 42d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianpublic class HttpFetcher { 43d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 44d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Phone number 45d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static final String PARAM_ID = "id"; 46d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // auth token 47d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static final String PARAM_ACCESS_TOKEN = "access_token"; 48d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static final String TAG = HttpFetcher.class.getSimpleName(); 49d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 50d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 51d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Send a http request to the given url. 52d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 53d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param urlString The url to request. 54d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return The response body as a byte array. Or {@literal null} if status code is not 2xx. 55d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @throws java.io.IOException when an error occurs. 56d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 57d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static byte[] sendRequestAsByteArray( 58d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Context context, String urlString, String requestMethod, List<Pair<String, String>> headers) 59d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws IOException, AuthException { 60d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Objects.requireNonNull(urlString); 61d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 62d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian URL url = reWriteUrl(context, urlString); 63d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (url == null) { 64d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 65d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 66d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 67d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian HttpURLConnection conn = null; 68d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian InputStream is = null; 69d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian boolean isError = false; 70d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final long start = SystemClock.uptimeMillis(); 71d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 72d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian conn = (HttpURLConnection) url.openConnection(); 73d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian setMethodAndHeaders(conn, requestMethod, headers); 74d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int responseCode = conn.getResponseCode(); 75d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.sendRequestAsByteArray", "response code: " + responseCode); 76d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // All 2xx codes are successful. 77d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (responseCode / 100 == 2) { 78d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian is = conn.getInputStream(); 79d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else { 80d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian is = conn.getErrorStream(); 81d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian isError = true; 82d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 83d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 84d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 85d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final byte[] buffer = new byte[1024]; 86d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int bytesRead; 87d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 88d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian while ((bytesRead = is.read(buffer)) != -1) { 89d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian baos.write(buffer, 0, bytesRead); 90d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 91d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 92d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (isError) { 93d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian handleBadResponse(url.toString(), baos.toByteArray()); 94d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (responseCode == 401) { 95d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new AuthException("Auth error"); 96d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 97d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 98d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 99d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 100d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian byte[] response = baos.toByteArray(); 101d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.sendRequestAsByteArray", "received " + response.length + " bytes"); 102d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian long end = SystemClock.uptimeMillis(); 103d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.sendRequestAsByteArray", "fetch took " + (end - start) + " ms"); 104d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return response; 105d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } finally { 106d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian closeQuietly(is); 107d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (conn != null) { 108d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian conn.disconnect(); 109d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 110d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 111d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 112d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 113d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 114d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Send a http request to the given url. 115d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 116d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return The response body as a InputStream. Or {@literal null} if status code is not 2xx. 117d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @throws java.io.IOException when an error occurs. 118d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 119d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static InputStream sendRequestAsInputStream( 120d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Context context, String urlString, String requestMethod, List<Pair<String, String>> headers) 121d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws IOException, AuthException { 122d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Objects.requireNonNull(urlString); 123d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 124d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian URL url = reWriteUrl(context, urlString); 125d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (url == null) { 126d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 127d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 128d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 129d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian HttpURLConnection httpUrlConnection = null; 130d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian boolean isSuccess = false; 131d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 132d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian httpUrlConnection = (HttpURLConnection) url.openConnection(); 133d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian setMethodAndHeaders(httpUrlConnection, requestMethod, headers); 134d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian int responseCode = httpUrlConnection.getResponseCode(); 135d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.sendRequestAsInputStream", "response code: " + responseCode); 136d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 137d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (responseCode == 401) { 138d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throw new AuthException("Auth error"); 139d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else if (responseCode / 100 == 2) { // All 2xx codes are successful. 140d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian InputStream is = httpUrlConnection.getInputStream(); 141d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (is != null) { 142d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian is = new HttpInputStreamWrapper(httpUrlConnection, is); 143d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian isSuccess = true; 144d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return is; 145d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 146d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 147d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 148d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 149d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } finally { 150d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (httpUrlConnection != null && !isSuccess) { 151d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian httpUrlConnection.disconnect(); 152d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 153d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 154d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 155d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 156d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 157d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Set http method and headers. 158d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 159d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param conn The connection to add headers to. 160d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param requestMethod request method 161d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param headers http headers where the first item in the pair is the key and second item is the 162d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * value. 163d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 164d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static void setMethodAndHeaders( 165d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian HttpURLConnection conn, String requestMethod, List<Pair<String, String>> headers) 166d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws ProtocolException { 167d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian conn.setRequestMethod(requestMethod); 168d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (headers != null) { 169d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (Pair<String, String> pair : headers) { 170d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian conn.setRequestProperty(pair.first, pair.second); 171d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 172d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 173d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 174d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 175d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static String obfuscateUrl(String urlString) { 176d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final Uri uri = Uri.parse(urlString); 177d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final Builder builder = 178d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian new Builder().scheme(uri.getScheme()).authority(uri.getAuthority()).path(uri.getPath()); 179d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final Set<String> names = uri.getQueryParameterNames(); 180d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian for (String name : names) { 181d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (PARAM_ACCESS_TOKEN.equals(name)) { 182d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian builder.appendQueryParameter(name, "token"); 183d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else { 184d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final String value = uri.getQueryParameter(name); 185d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (PARAM_ID.equals(name)) { 186d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian builder.appendQueryParameter(name, MoreStrings.toSafeString(value)); 187d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } else { 188d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian builder.appendQueryParameter(name, value); 189d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 190d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 191d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 192d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return builder.toString(); 193d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 194d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 195d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** Same as {@link #getRequestAsString(Context, String, String, List)} with null headers. */ 196d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String getRequestAsString(Context context, String urlString) 197d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws IOException, AuthException { 198d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return getRequestAsString(context, urlString, "GET" /* Default to get. */, null); 199d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 200d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 201d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 202d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Send a http request to the given url. 203d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 204d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param context The android context. 205d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param urlString The url to request. 206d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @param headers Http headers to pass in the request. {@literal null} is allowed. 207d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return The response body as a String. Or {@literal null} if status code is not 2xx. 208d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @throws java.io.IOException when an error occurs. 209d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 210d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public static String getRequestAsString( 211d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian Context context, String urlString, String requestMethod, List<Pair<String, String>> headers) 212d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian throws IOException, AuthException { 213d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final byte[] byteArr = sendRequestAsByteArray(context, urlString, requestMethod, headers); 214d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (byteArr == null) { 215d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Encountered error response... just return. 216d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 217d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 218d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final String response = new String(byteArr); 219d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.getRequestAsString", "response body: " + response); 220d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return response; 221d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 222d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 223d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** 224d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * Lookup up url re-write rules from gServices and apply to the given url. 225d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * 226fc37b02f5d3381a7882770941e461b13b679b6efEric Erfanian * <p>https://wiki.corp.google.com/twiki/bin/view/Main/AndroidGservices#URL_Rewriting_Rules 227fc37b02f5d3381a7882770941e461b13b679b6efEric Erfanian * 228d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian * @return The new url. 229d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian */ 230d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static URL reWriteUrl(Context context, String url) { 231d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final UrlRules rules = UrlRules.getRules(context.getContentResolver()); 232d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final UrlRules.Rule rule = rules.matchRule(url); 233d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final String newUrl = rule.apply(url); 234d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 235d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (newUrl == null) { 236d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (LogUtil.isDebugEnabled()) { 237d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian // Url is blocked by re-write. 238d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i( 239d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian "HttpFetcher.reWriteUrl", 240d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian "url " + obfuscateUrl(url) + " is blocked. Ignoring request."); 241d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 242d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return null; 243d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 244d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 245d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (LogUtil.isDebugEnabled()) { 246d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.reWriteUrl", "fetching " + obfuscateUrl(newUrl)); 247d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (!newUrl.equals(url)) { 248d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i( 249d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian "HttpFetcher.reWriteUrl", 250d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian "Original url: " + obfuscateUrl(url) + ", after re-write: " + obfuscateUrl(newUrl)); 251d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 252d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 253d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 254d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian URL urlObject = null; 255d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian try { 256d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian urlObject = new URL(newUrl); 257d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } catch (MalformedURLException e) { 258d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.e("HttpFetcher.reWriteUrl", "failed to parse url: " + url, e); 259d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 260d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return urlObject; 261d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 262d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 263d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static void handleBadResponse(String url, byte[] response) { 264d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.handleBadResponse", "Got bad response code from url: " + url); 265d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.handleBadResponse", new String(response)); 266d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 267d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 268d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian /** Disconnect {@link HttpURLConnection} when InputStream is closed */ 269d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian private static class HttpInputStreamWrapper extends FilterInputStream { 270d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 271d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final HttpURLConnection mHttpUrlConnection; 272d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian final long mStartMillis = SystemClock.uptimeMillis(); 273d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 274d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public HttpInputStreamWrapper(HttpURLConnection conn, InputStream in) { 275d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian super(in); 276d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHttpUrlConnection = conn; 277d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 278d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian 279d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian @Override 280d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian public void close() throws IOException { 281d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian super.close(); 282d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian mHttpUrlConnection.disconnect(); 283d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (LogUtil.isDebugEnabled()) { 284d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian long endMillis = SystemClock.uptimeMillis(); 285d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian LogUtil.i("HttpFetcher.close", "fetch took " + (endMillis - mStartMillis) + " ms"); 286d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 287d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 288d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian } 289d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian} 290