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