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