blob: 105a96dddda31a0869ff3d3db0bb7fe4415beb66 [file] [log] [blame]
package com.android.hotspot2.osu.service;
import android.util.Log;
import com.android.hotspot2.flow.PlatformAdapter;
import com.android.hotspot2.osu.OSUOperationStatus;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Random;
public class RedirectListener extends Thread {
private static final long ThreadTimeout = 3000L;
private static final long UserTimeout = 3600000L;
private static final int MaxRetry = 5;
private static final String TAG = "OSULSN";
private static final String HTTPResponseHeader =
"HTTP/1.1 304 Not Modified\r\n" +
"Server: dummy\r\n" +
"Keep-Alive: timeout=500, max=5\r\n\r\n";
private static final String GoodBye =
"<html>" +
"<head><title>Goodbye</title></head>" +
"<body>" +
"<h3>Killing browser...</h3>" +
"</body>" +
"</html>\r\n";
private final PlatformAdapter mPlatformAdapter;
private final String mSpName;
private final ServerSocket mServerSocket;
private final String mPath;
private final URL mURL;
private final Object mLock = new Object();
private boolean mListening;
private OSUOperationStatus mUserStatus;
private volatile boolean mAborted;
public RedirectListener(PlatformAdapter platformAdapter, String spName) throws IOException {
mPlatformAdapter = platformAdapter;
mSpName = spName;
mServerSocket = new ServerSocket(0, 5, InetAddress.getLocalHost());
Random rnd = new Random(System.currentTimeMillis());
mPath = "rnd" + Integer.toString(Math.abs(rnd.nextInt()), Character.MAX_RADIX);
mURL = new URL("http", mServerSocket.getInetAddress().getHostAddress(),
mServerSocket.getLocalPort(), mPath);
Log.d(TAG, "Redirect URL: " + mURL);
setName("HS20-Redirect-Listener");
setDaemon(true);
}
public void startService() throws IOException {
start();
synchronized (mLock) {
long bail = System.currentTimeMillis() + ThreadTimeout;
long remainder = ThreadTimeout;
while (remainder > 0 && !mListening) {
try {
mLock.wait(remainder);
} catch (InterruptedException ie) {
/**/
}
if (mListening) {
break;
}
remainder = bail - System.currentTimeMillis();
}
if (!mListening) {
throw new IOException("Failed to start listener");
} else {
Log.d(TAG, "OSU Redirect listener running");
}
}
}
public boolean waitForUser() {
boolean success;
synchronized (mLock) {
long bail = System.currentTimeMillis() + UserTimeout;
long remainder = UserTimeout;
while (remainder > 0 && mUserStatus == null) {
try {
mLock.wait(remainder);
} catch (InterruptedException ie) {
/**/
}
if (mUserStatus != null) {
break;
}
remainder = bail - System.currentTimeMillis();
}
success = mUserStatus == OSUOperationStatus.UserInputComplete;
}
abort();
return success;
}
public void abort() {
try {
synchronized (mLock) {
mUserStatus = OSUOperationStatus.UserInputAborted;
mLock.notifyAll();
}
mAborted = true;
mServerSocket.close();
} catch (IOException ioe) {
/**/
}
}
public URL getURL() {
return mURL;
}
@Override
public void run() {
int count = 0;
synchronized (mLock) {
mListening = true;
mLock.notifyAll();
}
boolean terminate = false;
for (; ; ) {
count++;
try (Socket instance = mServerSocket.accept()) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(instance.getInputStream(), StandardCharsets.UTF_8))) {
boolean detected = false;
StringBuilder sb = new StringBuilder();
String s;
while ((s = in.readLine()) != null) {
sb.append(s).append('\n');
if (!detected && s.startsWith("GET")) {
String[] segments = s.split(" ");
if (segments.length == 3 &&
segments[2].startsWith("HTTP/") &&
segments[1].regionMatches(1, mPath, 0, mPath.length())) {
detected = true;
}
}
if (s.length() == 0) {
break;
}
}
Log.d(TAG, "Redirect receive: " + sb);
String response = null;
if (detected) {
response = status(OSUOperationStatus.UserInputComplete);
if (response == null) {
response = GoodBye;
terminate = true;
}
}
try (BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(instance.getOutputStream(),
StandardCharsets.UTF_8))) {
out.write(HTTPResponseHeader);
if (response != null) {
out.write(response);
}
}
if (terminate) {
break;
} else if (count > MaxRetry) {
status(OSUOperationStatus.UserInputAborted);
break;
}
}
} catch (IOException ioe) {
if (mAborted) {
return;
} else if (count > MaxRetry) {
status(OSUOperationStatus.UserInputAborted);
break;
}
}
}
}
private String status(OSUOperationStatus status) {
Log.d(TAG, "User input status: " + status);
synchronized (mLock) {
mUserStatus = status;
mLock.notifyAll();
}
String message = (status == OSUOperationStatus.UserInputAborted) ?
"Browser closed" : null;
return mPlatformAdapter.notifyUser(status, message, mSpName);
}
}