1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.android_scripting.facade.wifi; 18 19 import com.googlecode.android_scripting.FileUtils; 20 import com.googlecode.android_scripting.Log; 21 import com.googlecode.android_scripting.facade.FacadeManager; 22 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 23 import com.googlecode.android_scripting.rpc.Rpc; 24 import com.googlecode.android_scripting.rpc.RpcOptional; 25 import com.googlecode.android_scripting.rpc.RpcParameter; 26 27 import java.io.BufferedInputStream; 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.InputStreamReader; 34 import java.io.OutputStream; 35 import java.net.HttpURLConnection; 36 import java.net.ServerSocket; 37 import java.net.Socket; 38 import java.net.SocketException; 39 import java.net.URL; 40 import java.net.UnknownHostException; 41 import java.nio.charset.StandardCharsets; 42 import java.util.HashMap; 43 44 45 /** 46 * Basic http operations. 47 */ 48 public class HttpFacade extends RpcReceiver { 49 50 private ServerSocket mServerSocket = null; 51 private int mServerTimeout = -1; 52 private HashMap<Integer, Socket> mSockets = null; 53 private int socketCnt = 0; 54 HttpFacade(FacadeManager manager)55 public HttpFacade(FacadeManager manager) throws IOException { 56 super(manager); 57 mSockets = new HashMap<Integer, Socket>(); 58 } 59 inputStreamToOutputStream(InputStream in, OutputStream out)60 private void inputStreamToOutputStream(InputStream in, OutputStream out) throws IOException { 61 if (in == null) { 62 Log.e("InputStream is null."); 63 return; 64 } 65 if (out == null) { 66 Log.e("OutputStream is null."); 67 return; 68 } 69 try { 70 int read = 0; 71 byte[] bytes = new byte[1024]; 72 while ((read = in.read(bytes)) != -1) { 73 out.write(bytes, 0, read); 74 } 75 } catch (IOException e) { 76 e.printStackTrace(); 77 } finally { 78 in.close(); 79 out.close(); 80 } 81 82 } 83 inputStreamToString(InputStream in)84 private String inputStreamToString(InputStream in) throws IOException { 85 BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 86 StringBuilder sb = new StringBuilder(); 87 String str = null; 88 while ((str = r.readLine()) != null) { 89 sb.append(str); 90 } 91 r.close(); 92 return sb.toString(); 93 } 94 95 /** 96 * Send an http request and get the response. 97 * 98 * @param url The url to send request to. 99 * @param timeout Time to load the page 100 * @return The HttpURLConnection object. 101 */ httpRequest(String url, Integer timeout)102 private HttpURLConnection httpRequest(String url, Integer timeout) throws IOException { 103 if (timeout == null) { 104 timeout = 50000; 105 } 106 URL targetURL = new URL(url); 107 HttpURLConnection urlConnection; 108 try { 109 urlConnection = (HttpURLConnection) targetURL.openConnection(); 110 urlConnection.setConnectTimeout(9000); 111 urlConnection.setReadTimeout(timeout); 112 urlConnection.connect(); 113 int respCode = urlConnection.getResponseCode(); 114 String respMsg = urlConnection.getResponseMessage(); 115 Log.d("Got response code: " + respCode + " and response msg: " + respMsg); 116 } catch (IOException e) { 117 Log.e("Failed to open a connection to " + url); 118 Log.e(e.toString()); 119 throw e; 120 } 121 return urlConnection; 122 } 123 124 @Rpc(description = "Start waiting for a connection request on a specified port.", 125 returns = "The index of the connection.") httpAcceptConnection(@pcParametername = "port") Integer port)126 public Integer httpAcceptConnection(@RpcParameter(name = "port") Integer port) throws IOException { 127 mServerSocket = new ServerSocket(port); 128 if (mServerTimeout > 0) { 129 mServerSocket.setSoTimeout(mServerTimeout); 130 } 131 Socket sock = mServerSocket.accept(); 132 socketCnt += 1; 133 mSockets.put(socketCnt, sock); 134 return socketCnt; 135 } 136 137 @Rpc(description = "Download a file from specified url, to an (optionally) specified path.") httpDownloadFile(@pcParametername = "url") String url, @RpcParameter(name="outPath") @RpcOptional String outPath)138 public void httpDownloadFile(@RpcParameter(name = "url") String url, 139 @RpcParameter(name="outPath") @RpcOptional String outPath) throws IOException { 140 // Create the input stream 141 HttpURLConnection urlConnection = httpRequest(url, null); 142 // Parse destination path and create the output stream. The function assumes that the path 143 // is specified relative to the system default Download dir. 144 File outFile = FileUtils.getExternalDownload(); 145 if (outPath != null && outPath.trim().length() != 0) { 146 // Check to see if the path is absolute. 147 if (outPath.startsWith("/")) { 148 outFile = new File(outPath); 149 } else { 150 outFile = new File(outFile, outPath); 151 } 152 // Check to see if specified path should be a dir. 153 if (outPath.endsWith("/")) { 154 if (!outFile.isDirectory() && !outFile.mkdirs()) { 155 throw new IOException("Failed to create the path: " + outPath); 156 } 157 } 158 } 159 // If no filename was specified, use the filename provided by the server. 160 if (outFile.isDirectory()) { 161 String filename = ""; 162 String contentDisposition = urlConnection.getHeaderField("Content-Disposition"); 163 // Try to figure out the name of the file being downloaded. 164 // If the server returned a filename, use it. 165 if (contentDisposition != null) { 166 int idx = contentDisposition.toLowerCase().indexOf("filename"); 167 if (idx != -1) { 168 filename = contentDisposition.substring(idx + 9); 169 Log.d("Using filename returned by server: " + filename); 170 } 171 } 172 // If the server did not provide a filename to us, use the last part of url. 173 if (filename.trim().length() == 0) { 174 int lastIdx = url.lastIndexOf('/'); 175 filename = url.substring(lastIdx + 1); 176 Log.d("Using name from url: " + filename); 177 } 178 outFile = new File(outFile, filename); 179 } 180 InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 181 OutputStream output = new FileOutputStream(outFile); 182 inputStreamToOutputStream(in, output); 183 Log.d("Downloaded file from " + url + " to " + outPath); 184 urlConnection.disconnect(); 185 } 186 187 @Rpc(description = "Make an http request and return the response message.") httpPing( @pcParametername = "url") String url, @RpcParameter(name = "timeout") @RpcOptional Integer timeout)188 public HttpURLConnection httpPing( 189 @RpcParameter(name = "url") String url, 190 @RpcParameter(name = "timeout") @RpcOptional Integer timeout) throws IOException { 191 HttpURLConnection urlConnection = null; 192 urlConnection = httpRequest(url, timeout); 193 urlConnection.disconnect(); 194 return urlConnection; 195 } 196 197 @Rpc(description = "Make an http request and return the response content as a string.") httpRequestString(@pcParametername = "url") String url)198 public String httpRequestString(@RpcParameter(name = "url") String url) throws IOException { 199 HttpURLConnection urlConnection = httpRequest(url, null); 200 InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 201 String result = inputStreamToString(in); 202 Log.d("Fetched: " + result); 203 urlConnection.disconnect(); 204 return result; 205 } 206 207 @Rpc(description = "Set how many milliseconds to wait for an incoming connection.") httpSetServerTimeout(@pcParametername = "timeout") Integer timeout)208 public void httpSetServerTimeout(@RpcParameter(name = "timeout") Integer timeout) 209 throws SocketException { 210 mServerSocket.setSoTimeout(timeout); 211 mServerTimeout = timeout; 212 } 213 214 @Rpc(description = "Ping to host(URL or IP), return success (true) or fail (false).") 215 // The optional timeout parameter is in unit of second. pingHost(@pcParametername = "host") String hostString, @RpcParameter(name = "timeout") @RpcOptional Integer timeout, @RpcParameter(name = "ping") @RpcOptional String pingType)216 public Boolean pingHost(@RpcParameter(name = "host") String hostString, 217 @RpcParameter(name = "timeout") @RpcOptional Integer timeout, 218 @RpcParameter(name = "ping") @RpcOptional String pingType) { 219 try { 220 String host; 221 try { 222 URL url = new URL(hostString); 223 host = url.getHost(); 224 } catch (java.net.MalformedURLException e) { 225 Log.d("hostString is not URL, it may be IP address."); 226 host = hostString; 227 } 228 229 Log.d("Host:" + host); 230 String pingCmdString = "ping -c 1 "; 231 if(pingType!=null && pingType.equals("ping6")) { 232 pingCmdString = "ping6 -c 1 "; 233 } 234 if (timeout != null) { 235 pingCmdString = pingCmdString + "-W " + timeout + " "; 236 } 237 pingCmdString = pingCmdString + host; 238 Log.d("Execute command: " + pingCmdString); 239 Process p1 = java.lang.Runtime.getRuntime().exec(pingCmdString); 240 int returnVal = p1.waitFor(); 241 boolean reachable = (returnVal == 0); 242 Log.d("Ping return Value:" + returnVal); 243 return reachable; 244 } catch (Exception e){ 245 e.printStackTrace(); 246 return false; 247 } 248 /*TODO see b/18899134 for more information. 249 */ 250 } 251 252 @Override shutdown()253 public void shutdown() { 254 for (int key : mSockets.keySet()) { 255 Socket sock = mSockets.get(key); 256 try { 257 sock.close(); 258 } catch (IOException e) { 259 Log.e("Failed to close socket " + key + " on port " + sock.getLocalPort()); 260 e.printStackTrace(); 261 } 262 } 263 } 264 } 265