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 55 public HttpFacade(FacadeManager manager) throws IOException { 56 super(manager); 57 mSockets = new HashMap<Integer, Socket>(); 58 } 59 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 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 * @return The HttpURLConnection object. 100 */ 101 private HttpURLConnection httpRequest(String url) throws IOException { 102 URL targetURL = new URL(url); 103 HttpURLConnection urlConnection = null; 104 try { 105 urlConnection = (HttpURLConnection) targetURL.openConnection(); 106 urlConnection.connect(); 107 int respCode = urlConnection.getResponseCode(); 108 String respMsg = urlConnection.getResponseMessage(); 109 Log.d("Got response code: " + respCode + " and response msg: " + respMsg); 110 } catch (IOException e) { 111 Log.e("Failed to open a connection to " + url); 112 Log.e(e.toString()); 113 } 114 return urlConnection; 115 } 116 117 @Rpc(description = "Start waiting for a connection request on a specified port.", 118 returns = "The index of the connection.") 119 public Integer httpAcceptConnection(@RpcParameter(name = "port") Integer port) throws IOException { 120 mServerSocket = new ServerSocket(port); 121 if (mServerTimeout > 0) { 122 mServerSocket.setSoTimeout(mServerTimeout); 123 } 124 Socket sock = mServerSocket.accept(); 125 socketCnt += 1; 126 mSockets.put(socketCnt, sock); 127 return socketCnt; 128 } 129 130 @Rpc(description = "Download a file from specified url, to an (optionally) specified path.") 131 public void httpDownloadFile(@RpcParameter(name = "url") String url, 132 @RpcParameter(name="outPath") @RpcOptional String outPath) throws IOException { 133 // Create the input stream 134 HttpURLConnection urlConnection = httpRequest(url); 135 // Parse destination path and create the output stream. The function assumes that the path 136 // is specified relative to the system default Download dir. 137 File outFile = FileUtils.getExternalDownload(); 138 if (outPath != null && outPath.trim().length() != 0) { 139 // Check to see if the path is absolute. 140 if (outPath.startsWith("/")) { 141 outFile = new File(outPath); 142 } else { 143 outFile = new File(outFile, outPath); 144 } 145 // Check to see if specified path should be a dir. 146 if (outPath.endsWith("/")) { 147 if (!outFile.isDirectory() && !outFile.mkdirs()) { 148 throw new IOException("Failed to create the path: " + outPath); 149 } 150 } 151 } 152 // If no filename was specified, use the filename provided by the server. 153 if (outFile.isDirectory()) { 154 String filename = ""; 155 String contentDisposition = urlConnection.getHeaderField("Content-Disposition"); 156 // Try to figure out the name of the file being downloaded. 157 // If the server returned a filename, use it. 158 if (contentDisposition != null) { 159 int idx = contentDisposition.toLowerCase().indexOf("filename"); 160 if (idx != -1) { 161 filename = contentDisposition.substring(idx + 9); 162 Log.d("Using filename returned by server: " + filename); 163 } 164 } 165 // If the server did not provide a filename to us, use the last part of url. 166 if (filename.trim().length() == 0) { 167 int lastIdx = url.lastIndexOf('/'); 168 filename = url.substring(lastIdx + 1); 169 Log.d("Using name from url: " + filename); 170 } 171 outFile = new File(outFile, filename); 172 } 173 InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 174 OutputStream output = new FileOutputStream(outFile); 175 inputStreamToOutputStream(in, output); 176 Log.d("Downloaded file from " + url + " to " + outPath); 177 urlConnection.disconnect(); 178 } 179 180 @Rpc(description = "Make an http request and return the response message.") 181 public HttpURLConnection httpPing(@RpcParameter(name = "url") String url) throws IOException { 182 try { 183 HttpURLConnection urlConnection = null; 184 urlConnection = httpRequest(url); 185 urlConnection.disconnect(); 186 return urlConnection; 187 } catch (UnknownHostException e) { 188 return null; 189 } 190 } 191 192 @Rpc(description = "Make an http request and return the response content as a string.") 193 public String httpRequestString(@RpcParameter(name = "url") String url) throws IOException { 194 HttpURLConnection urlConnection = httpRequest(url); 195 InputStream in = new BufferedInputStream(urlConnection.getInputStream()); 196 String result = inputStreamToString(in); 197 Log.d("Fetched: " + result); 198 urlConnection.disconnect(); 199 return result; 200 } 201 202 @Rpc(description = "Set how many milliseconds to wait for an incoming connection.") 203 public void httpSetServerTimeout(@RpcParameter(name = "timeout") Integer timeout) 204 throws SocketException { 205 mServerSocket.setSoTimeout(timeout); 206 mServerTimeout = timeout; 207 } 208 209 @Rpc(description = "Ping to host(URL or IP), return success (true) or fail (false).") 210 // The optional timeout parameter is in unit of second. 211 public Boolean pingHost(@RpcParameter(name = "host") String hostString, 212 @RpcParameter(name = "timeout") @RpcOptional Integer timeout, 213 @RpcParameter(name = "ping") @RpcOptional String pingType) { 214 try { 215 String host; 216 try { 217 URL url = new URL(hostString); 218 host = url.getHost(); 219 } catch (java.net.MalformedURLException e) { 220 Log.d("hostString is not URL, it may be IP address."); 221 host = hostString; 222 } 223 224 Log.d("Host:" + host); 225 String pingCmdString = "ping -c 1 "; 226 if(pingType!=null && pingType.equals("ping6")) { 227 pingCmdString = "ping6 -c 1 "; 228 } 229 if (timeout != null) { 230 pingCmdString = pingCmdString + "-W " + timeout + " "; 231 } 232 pingCmdString = pingCmdString + host; 233 Log.d("Execute command: " + pingCmdString); 234 Process p1 = java.lang.Runtime.getRuntime().exec(pingCmdString); 235 int returnVal = p1.waitFor(); 236 boolean reachable = (returnVal == 0); 237 Log.d("Ping return Value:" + returnVal); 238 return reachable; 239 } catch (Exception e){ 240 e.printStackTrace(); 241 return false; 242 } 243 /*TODO see b/18899134 for more information. 244 */ 245 } 246 247 @Override 248 public void shutdown() { 249 for (int key : mSockets.keySet()) { 250 Socket sock = mSockets.get(key); 251 try { 252 sock.close(); 253 } catch (IOException e) { 254 Log.e("Failed to close socket " + key + " on port " + sock.getLocalPort()); 255 e.printStackTrace(); 256 } 257 } 258 } 259 } 260