1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage com.android.hotspot2.osu.service; 2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.util.Log; 4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.osu.OSUManager; 6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport com.android.hotspot2.osu.OSUOperationStatus; 7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.BufferedReader; 9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.BufferedWriter; 10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.IOException; 11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.InputStreamReader; 12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.OutputStreamWriter; 13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.InetAddress; 14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.ServerSocket; 15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.Socket; 16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.URL; 17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.nio.charset.StandardCharsets; 18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Random; 19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic class RedirectListener extends Thread { 21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final long ThreadTimeout = 3000L; 22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final long UserTimeout = 3600000L; 23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final int MaxRetry = 5; 24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final String TAG = "OSULSN"; 25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final String HTTPResponseHeader = 27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "HTTP/1.1 304 Not Modified\r\n" + 28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "Server: dummy\r\n" + 29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "Keep-Alive: timeout=500, max=5\r\n\r\n"; 30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static final String GoodBye = 32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "<html>" + 33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "<head><title>Goodbye</title></head>" + 34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "<body>" + 35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "<h3>Killing browser...</h3>" + 36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "</body>" + 37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "</html>\r\n"; 38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final OSUManager mOSUManager; 40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mSpName; 41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final ServerSocket mServerSocket; 42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mPath; 43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final URL mURL; 44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Object mLock = new Object(); 45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private boolean mListening; 47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private OSUOperationStatus mUserStatus; 48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private volatile boolean mAborted; 49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public RedirectListener(OSUManager osuManager, String spName) throws IOException { 51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mOSUManager = osuManager; 52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mSpName = spName; 53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mServerSocket = new ServerSocket(0, 5, InetAddress.getLocalHost()); 54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Random rnd = new Random(System.currentTimeMillis()); 55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mPath = "rnd" + Integer.toString(Math.abs(rnd.nextInt()), Character.MAX_RADIX); 56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mURL = new URL("http", mServerSocket.getInetAddress().getHostAddress(), 57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mServerSocket.getLocalPort(), mPath); 58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(TAG, "Redirect URL: " + mURL); 60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist setName("HS20-Redirect-Listener"); 61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist setDaemon(true); 62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public void startService() throws IOException { 65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist start(); 66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist synchronized (mLock) { 67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist long bail = System.currentTimeMillis() + ThreadTimeout; 68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist long remainder = ThreadTimeout; 69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist while (remainder > 0 && !mListening) { 70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try { 71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mLock.wait(remainder); 72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (InterruptedException ie) { 73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /**/ 74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (mListening) { 76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist remainder = bail - System.currentTimeMillis(); 79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!mListening) { 81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("Failed to start listener"); 82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else { 83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(TAG, "OSU Redirect listener running"); 84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public boolean waitForUser() { 89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist boolean success; 90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist synchronized (mLock) { 91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist long bail = System.currentTimeMillis() + UserTimeout; 92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist long remainder = UserTimeout; 93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist while (remainder > 0 && mUserStatus == null) { 94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try { 95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mLock.wait(remainder); 96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (InterruptedException ie) { 97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /**/ 98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (mUserStatus != null) { 100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist remainder = bail - System.currentTimeMillis(); 103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist success = mUserStatus == OSUOperationStatus.UserInputComplete; 105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist abort(); 107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return success; 108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public void abort() { 111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try { 112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mAborted = true; 113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mServerSocket.close(); 114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (IOException ioe) { 115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist /**/ 116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public URL getURL() { 120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mURL; 121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public void run() { 125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int count = 0; 126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist synchronized (mLock) { 127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mListening = true; 128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mLock.notifyAll(); 129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist boolean terminate = false; 132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (; ; ) { 134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist count++; 135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try (Socket instance = mServerSocket.accept()) { 136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try (BufferedReader in = new BufferedReader( 137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist new InputStreamReader(instance.getInputStream(), StandardCharsets.UTF_8))) { 138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist boolean detected = false; 139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist StringBuilder sb = new StringBuilder(); 140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String s; 141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist while ((s = in.readLine()) != null) { 142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist sb.append(s).append('\n'); 143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (!detected && s.startsWith("GET")) { 144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String[] segments = s.split(" "); 145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (segments.length == 3 && 146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist segments[2].startsWith("HTTP/") && 147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist segments[1].regionMatches(1, mPath, 0, mPath.length())) { 148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist detected = true; 149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (s.length() == 0) { 152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(TAG, "Redirect receive: " + sb); 156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String response = null; 157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (detected) { 158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist response = status(OSUOperationStatus.UserInputComplete); 159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (response == null) { 160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist response = GoodBye; 161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist terminate = true; 162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try (BufferedWriter out = new BufferedWriter( 165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist new OutputStreamWriter(instance.getOutputStream(), 166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist StandardCharsets.UTF_8))) { 167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist out.write(HTTPResponseHeader); 169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (response != null) { 170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist out.write(response); 171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 173ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (terminate) { 174ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 175ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else if (count > MaxRetry) { 176ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist status(OSUOperationStatus.UserInputAborted); 177ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 178ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 179ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 180ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (IOException ioe) { 181ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (mAborted) { 182ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return; 183ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else if (count > MaxRetry) { 184ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist status(OSUOperationStatus.UserInputAborted); 185ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist break; 186ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 187ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 188ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 189ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 190ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 191ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private String status(OSUOperationStatus status) { 192ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.d(TAG, "User input status: " + status); 193ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist synchronized (mLock) { 194ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mUserStatus = status; 195ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mLock.notifyAll(); 196ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 197ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String message = (status == OSUOperationStatus.UserInputAborted) ? 198ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist "Browser closed" : null; 199ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 200ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mOSUManager.notifyUser(status, message, mSpName); 201ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 202ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist} 203