", "");
74 | result = result.replaceAll("", "");
75 | result = result.replaceAll("
", "");
76 | result = result.replaceAll("", "");
77 |
78 | result = result.replaceAll("\n\n", "\n");
79 | return result.trim();
80 | }
81 |
82 |
83 | // 15ms
84 | private static InetAddress[] getByCommand() {
85 | try {
86 | Process process = Runtime.getRuntime().exec("getprop");
87 | InputStream inputStream = process.getInputStream();
88 | LineNumberReader lnr = new LineNumberReader(
89 | new InputStreamReader(inputStream));
90 | String line = null;
91 | ArrayList servers = new ArrayList(5);
92 | while ((line = lnr.readLine()) != null) {
93 | int split = line.indexOf("]: [");
94 | if (split == -1) {
95 | continue;
96 | }
97 | String property = line.substring(1, split);
98 | String value = line.substring(split + 4, line.length() - 1);
99 | if (property.endsWith(".dns") || property.endsWith(".dns1") ||
100 | property.endsWith(".dns2") || property.endsWith(".dns3") ||
101 | property.endsWith(".dns4")) {
102 |
103 | // normalize the address
104 |
105 | InetAddress ip = InetAddress.getByName(value);
106 |
107 | if (ip == null) continue;
108 |
109 | value = ip.getHostAddress();
110 |
111 | if (value == null) continue;
112 | if (value.length() == 0) continue;
113 |
114 | servers.add(ip);
115 | }
116 | }
117 | if (servers.size() > 0) {
118 | return servers.toArray(new InetAddress[servers.size()]);
119 | }
120 | } catch (IOException e) {
121 | Logger.getLogger("AndroidDnsServer").log(Level.WARNING, "Exception in findDNSByExec", e);
122 | }
123 | return null;
124 | }
125 |
126 | // 1ms
127 | private static InetAddress[] getByReflection() {
128 | try {
129 | Class> SystemProperties =
130 | Class.forName("android.os.SystemProperties");
131 | Method method = SystemProperties.getMethod("get",
132 | new Class>[]{String.class});
133 |
134 | ArrayList servers = new ArrayList(5);
135 |
136 | for (String propKey : new String[]{
137 | "net.dns1", "net.dns2", "net.dns3", "net.dns4"}) {
138 |
139 | String value = (String) method.invoke(null, propKey);
140 |
141 | if (value == null) continue;
142 | if (value.length() == 0) continue;
143 |
144 | InetAddress ip = InetAddress.getByName(value);
145 |
146 | if (ip == null) continue;
147 |
148 | value = ip.getHostAddress();
149 |
150 | if (value == null) continue;
151 | if (value.length() == 0) continue;
152 | if (servers.contains(ip)) continue;
153 |
154 | servers.add(ip);
155 | }
156 |
157 | if (servers.size() > 0) {
158 | return servers.toArray(new InetAddress[servers.size()]);
159 | }
160 | } catch (Exception e) {
161 | // we might trigger some problems this way
162 | Logger.getLogger("AndroidDnsServer").log(Level.WARNING, "Exception in findDNSByReflection", e);
163 | }
164 | return null;
165 | }
166 |
167 |
168 | public static String[] local() {
169 | InetAddress[] addresses = getByReflection();
170 | if (addresses == null) {
171 | addresses = getByCommand();
172 | if (addresses == null) {
173 | return new String[0];
174 | }
175 | }
176 | String[] ret = new String[addresses.length];
177 | for (int i = 0; i < ret.length; i++) {
178 | ret[i] = addresses[i].toString();
179 | if (ret[i].indexOf('/') == 0) {
180 | ret[i] = ret[i].substring(1);
181 | }
182 | }
183 | return ret;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/EnvInfo.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.ActivityManager;
5 | import android.content.Context;
6 | import android.os.Build;
7 | import android.os.Process;
8 | import android.util.Log;
9 |
10 | import java.io.BufferedReader;
11 | import java.io.FileNotFoundException;
12 | import java.io.FileReader;
13 | import java.io.IOException;
14 | import java.util.List;
15 |
16 | /**
17 | * Created by bailong on 16/3/14.
18 | */
19 | public final class EnvInfo {
20 | public static CpuInfo cpuInfo() {
21 | BufferedReader reader = null;
22 | long work1, total1, work2, total2;
23 | try {
24 | reader = new BufferedReader(new FileReader("/proc/stat"));
25 | String[] sa = reader.readLine().split("[ ]+", 9);
26 | work1 = Long.parseLong(sa[1]) + Long.parseLong(sa[2]) + Long.parseLong(sa[3]);
27 | total1 = work1 + Long.parseLong(sa[4]) + Long.parseLong(sa[5]) + Long.parseLong(sa[6]) + Long.parseLong(sa[7]);
28 | } catch (IOException e) {
29 | e.printStackTrace();
30 | return new CpuInfo(0, 0);
31 | } finally {
32 | if (reader != null) {
33 | try {
34 | reader.close();
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | }
38 | }
39 | }
40 |
41 | reader = null;
42 | long workP1;
43 | try {
44 | reader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/stat"));
45 | String[] sa = reader.readLine().split("[ ]+", 18);
46 | workP1 = Long.parseLong(sa[13]) + Long.parseLong(sa[14]) + Long.parseLong(sa[15]) + Long.parseLong(sa[16]);
47 | reader.close();
48 | } catch (IOException e) {
49 | e.printStackTrace();
50 | return new CpuInfo(0, 0);
51 | } finally {
52 | if (reader != null) {
53 | try {
54 | reader.close();
55 | } catch (IOException e) {
56 | e.printStackTrace();
57 | }
58 | }
59 | }
60 |
61 |
62 | try {
63 | Thread.sleep(100);
64 | } catch (InterruptedException e) {
65 | e.printStackTrace();
66 | }
67 | reader = null;
68 | try {
69 | reader = new BufferedReader(new FileReader("/proc/stat"));
70 | String[] sa = reader.readLine().split("[ ]+", 9);
71 | work2 = Long.parseLong(sa[1]) + Long.parseLong(sa[2]) + Long.parseLong(sa[3]);
72 | total2 = work2 + Long.parseLong(sa[4]) + Long.parseLong(sa[5]) + Long.parseLong(sa[6]) + Long.parseLong(sa[7]);
73 | } catch (IOException e) {
74 | e.printStackTrace();
75 | return new CpuInfo(0, 0);
76 | } finally {
77 | if (reader != null) {
78 | try {
79 | reader.close();
80 | } catch (IOException e) {
81 | e.printStackTrace();
82 | }
83 | }
84 | }
85 |
86 | reader = null;
87 | long workP2;
88 | try {
89 | reader = new BufferedReader(new FileReader("/proc/" + Process.myPid() + "/stat"));
90 | String[] sa = reader.readLine().split("[ ]+", 18);
91 | workP2 = Long.parseLong(sa[13]) + Long.parseLong(sa[14]) + Long.parseLong(sa[15]) + Long.parseLong(sa[16]);
92 | reader.close();
93 | } catch (IOException e) {
94 | e.printStackTrace();
95 | return new CpuInfo(0, 0);
96 | } finally {
97 | if (reader != null) {
98 | try {
99 | reader.close();
100 | } catch (IOException e) {
101 | e.printStackTrace();
102 | }
103 | }
104 | }
105 | long t = total2 - total1;
106 | float percent = (work2 - work1) * 100 / (float) t;
107 | float currentPercent = (workP2 - workP1) * 100 / (float) t;
108 | if (percent < 0 || percent > 100) {
109 | return new CpuInfo(0, 0);
110 | }
111 | return new CpuInfo(percent, currentPercent);
112 | }
113 |
114 | public static SystemMemInfo systemMemInfo() {
115 | BufferedReader reader = null;
116 | try {
117 | reader = new BufferedReader(new FileReader("/proc/meminfo"));
118 | } catch (FileNotFoundException e) {
119 | e.printStackTrace();
120 | return new SystemMemInfo();
121 | }
122 | int total = 0;
123 | int free = 0;
124 | int cached = 0;
125 | try {
126 | String s = reader.readLine();
127 | while (s != null) {
128 | if (s.startsWith("MemTotal:")) {
129 | total = Integer.parseInt(s.split("[ ]+", 3)[1]);
130 | } else if (s.startsWith("MemFree:"))
131 | free = Integer.parseInt(s.split("[ ]+", 3)[1]);
132 |
133 | else if (s.startsWith("Cached:")) {
134 | cached = Integer.parseInt(s.split("[ ]+", 3)[1]);
135 | }
136 | s = reader.readLine();
137 | }
138 | } catch (Exception e) {
139 | return new SystemMemInfo();
140 | } finally {
141 |
142 | try {
143 | reader.close();
144 | } catch (IOException e) {
145 | e.printStackTrace();
146 | }
147 | }
148 | return new SystemMemInfo(total, free, cached);
149 | }
150 |
151 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
152 | public static AppMemInfo memInfo(Context ctx) {
153 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
154 | return new AppMemInfo(0, 0, 0);
155 | }
156 | ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
157 | if (am == null) {
158 | return new AppMemInfo(0, 0, 0);
159 | }
160 | ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
161 | am.getMemoryInfo(mi);
162 |
163 | return new AppMemInfo(mi.totalMem, mi.totalMem - mi.availMem, mi.threshold);
164 |
165 | }
166 |
167 | @TargetApi(Build.VERSION_CODES.CUPCAKE)
168 | public static boolean isBackground(Context context) {
169 | if (context == null) {
170 | return true;
171 | }
172 | String pkgName = context.getPackageName();
173 | if (pkgName == null) {
174 | return true;
175 | }
176 | ActivityManager activityManager = (ActivityManager) context
177 | .getSystemService(Context.ACTIVITY_SERVICE);
178 | List appProcesses = activityManager
179 | .getRunningAppProcesses();
180 |
181 | for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
182 | if (pkgName.equals(appProcess.processName)) {
183 | if (appProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
184 | Log.i(context.getPackageName(), "Background"
185 | + appProcess.processName);
186 | return true;
187 | } else {
188 | Log.i(context.getPackageName(), "Foreground"
189 | + appProcess.processName);
190 | return false;
191 | }
192 | }
193 | }
194 |
195 | return false;
196 | }
197 |
198 | public static class CpuInfo {
199 | public final float total;
200 | public final float current;
201 |
202 | public CpuInfo(float total, float current) {
203 | this.total = total;
204 | this.current = current;
205 | }
206 | }
207 |
208 | public static class SystemMemInfo {
209 | public final int total;
210 | public final int free;
211 | public final int cached;
212 |
213 | public SystemMemInfo(int total, int free, int cached) {
214 | this.total = total;
215 | this.free = free;
216 | this.cached = cached;
217 | }
218 |
219 | public SystemMemInfo() {
220 | this(0, 0, 0);
221 | }
222 | }
223 |
224 | public static class AppMemInfo {
225 | public final long total;
226 | public final long used;
227 | public final long threshold;
228 |
229 | public AppMemInfo(long total, long used, long threshold) {
230 | this.total = total;
231 | this.used = used;
232 | this.threshold = threshold;
233 | }
234 | }
235 |
236 |
237 | }
238 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/HttpPing.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.net.HttpURLConnection;
6 | import java.net.URL;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | /**
11 | * Created by bailong on 16/2/24.
12 | */
13 | public final class HttpPing implements Task {
14 | private static final int MAX = 64 * 1024;
15 |
16 | private final Output out;
17 | private final String url;
18 | private final Callback complete;
19 | private volatile boolean stopped;
20 |
21 | private HttpPing(String url, Output out, final Callback complete) {
22 | this.out = out;
23 | this.url = url;
24 | this.complete = complete;
25 | this.stopped = false;
26 | }
27 |
28 | public static Task start(String url, Output out, Callback complete) {
29 | final HttpPing h = new HttpPing(url, out, complete);
30 | Util.runInBack(new Runnable() {
31 | @Override
32 | public void run() {
33 | h.run();
34 | }
35 | });
36 | return h;
37 | }
38 |
39 | private void run() {
40 | long start = System.currentTimeMillis();
41 | try {
42 | out.write("Get " + url);
43 | URL u = new URL(url);
44 | HttpURLConnection httpConn = (HttpURLConnection) u.openConnection();
45 | httpConn.setConnectTimeout(10000);
46 | httpConn.setReadTimeout(20000);
47 | int responseCode = httpConn.getResponseCode();
48 | out.write("status " + responseCode);
49 |
50 | Map> headers = httpConn.getHeaderFields();
51 | for (Map.Entry> entry : headers.entrySet()) {
52 | out.write(entry.getKey() + ":" + entry.getValue().get(0));
53 | }
54 | InputStream is = httpConn.getInputStream();
55 | int len = httpConn.getContentLength();
56 | len = len > MAX || len < 0 ? MAX : len;
57 | byte[] data = new byte[len];
58 | int read = is.read(data);
59 | long duration = System.currentTimeMillis() - start;
60 | out.write("Done, duration " + duration + "ms");
61 | is.close();
62 | if (read <= 0) {
63 | Result r = new Result(responseCode, headers, null, (int) duration, "no body");
64 | this.complete.complete(r);
65 | return;
66 | }
67 | if (read < data.length) {
68 | byte[] b = new byte[read];
69 | System.arraycopy(data, 0, b, 0, read);
70 | Result r = new Result(responseCode, headers, b, (int) duration, "no body");
71 | this.complete.complete(r);
72 |
73 | }
74 | } catch (IOException e) {
75 | e.printStackTrace();
76 | long duration = System.currentTimeMillis() - start;
77 | Result r = new Result(-1, null, null, (int) duration, e.getMessage());
78 | out.write("error : " + e.getMessage());
79 | this.complete.complete(r);
80 |
81 | }
82 | }
83 |
84 | @Override
85 | public void stop() {
86 | stopped = true;
87 | }
88 |
89 | public interface Callback {
90 | void complete(Result result);
91 | }
92 |
93 | public static class Result {
94 | public final int code;
95 | public final Map> headers;
96 | public final byte[] body;
97 | public final int duration;
98 | public final String errorMessage;
99 |
100 | private Result(int code,
101 | Map> headers, byte[] body, int duration, String errorMessage) {
102 | this.code = code;
103 | this.headers = headers;
104 | this.body = body;
105 | this.duration = duration;
106 | this.errorMessage = errorMessage;
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/IP.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.IOException;
4 | import java.net.DatagramSocket;
5 | import java.net.InetAddress;
6 |
7 | /**
8 | * Created by bailong on 16/2/24.
9 | */
10 | public final class IP {
11 | public static String external() throws IOException {
12 | return Util.httpGetString("http://whatismyip.akamai.com");
13 | }
14 |
15 | public static String local() {
16 | DatagramSocket socket;
17 | try {
18 | socket = new DatagramSocket();
19 | InetAddress addr = InetAddress.getByName("114.114.114.114");
20 | socket.connect(addr, 53);
21 | } catch (IOException e) {
22 | e.printStackTrace();
23 | return "";
24 | }
25 |
26 | InetAddress local = socket.getLocalAddress();
27 | socket.close();
28 | return local.getHostAddress();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/NsLookup.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import com.qiniu.android.netdiag.localdns.Record;
4 | import com.qiniu.android.netdiag.localdns.Resolver;
5 |
6 | import java.io.IOException;
7 | import java.net.InetAddress;
8 | import java.net.UnknownHostException;
9 |
10 | /**
11 | * Created by bailong on 16/2/24.
12 | */
13 | public final class NsLookup {
14 | private final String domain;
15 | private final String serverIp;
16 | private final Output output;
17 | private final Callback complete;
18 | // private volatile boolean stopped;
19 |
20 | private NsLookup(String domain, String serverIp, Output output, Callback complete) {
21 | this.domain = domain;
22 | this.serverIp = serverIp;
23 | this.output = output;
24 | this.complete = complete;
25 | }
26 |
27 | public static void start(String domain, Output output, Callback complete) {
28 | start(domain, null, output, complete);
29 | }
30 |
31 | public static void start(String domain, String serverIp, Output output, Callback complete) {
32 | if (serverIp == null) {
33 | String[] s = DNS.local();
34 | if (s != null) {
35 | serverIp = s[0];
36 | }
37 | }
38 | final NsLookup t = new NsLookup(domain, serverIp, output, complete);
39 | Util.runInBack(new Runnable() {
40 | @Override
41 | public void run() {
42 | t.run();
43 | }
44 | });
45 | }
46 |
47 | private void run() {
48 | if (serverIp == null) {
49 | Result r = new Result(-1, 0, null);
50 | complete.complete(r);
51 | return;
52 | }
53 |
54 | Resolver r = null;
55 | output.write("nslookup " + domain + " @" + serverIp);
56 | try {
57 | r = new Resolver(InetAddress.getByName(serverIp));
58 | } catch (UnknownHostException e) {
59 | Result result = new Result(-1, 0, null);
60 | output.write("nslookup server invalid");
61 | complete.complete(result);
62 | return;
63 | }
64 |
65 | try {
66 | long start = System.currentTimeMillis();
67 | Record[] records = r.resolve(domain);
68 | long duration = System.currentTimeMillis() - start;
69 | for (Record record : records) {
70 | output.write(record.toString());
71 | }
72 | complete.complete(new Result(0, (int) duration, records));
73 | } catch (IOException e) {
74 | e.printStackTrace();
75 | complete.complete(new Result(-3, 0, null));
76 | }
77 |
78 | }
79 |
80 | public interface Callback {
81 | void complete(Result result);
82 | }
83 |
84 | public static class Result {
85 | public final int code;
86 | public final int duration;
87 | public final Record[] records;
88 |
89 | private Result(int code, int duration, Record[] records) {
90 | this.code = code;
91 | this.duration = duration;
92 | this.records = records;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/Output.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | /**
4 | * Created by bailong on 16/2/24.
5 | */
6 | public interface Output {
7 | void write(String line);
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/Ping.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.net.InetAddress;
7 | import java.net.UnknownHostException;
8 | import java.util.Locale;
9 |
10 | /**
11 | * Created by bailong on 16/2/24.
12 | */
13 | public final class Ping implements Task {
14 | private final String address;
15 | private final int count;
16 | private final int size;
17 | private final Output output;
18 | private final Callback complete;
19 | private volatile boolean stopped;
20 | private int interval;
21 |
22 | private Ping(String address, int count,
23 |
24 | Output output, Callback complete) {
25 | this(address, count, 56, 200, output, complete);
26 | }
27 |
28 | private Ping(String address, int count, int size,
29 | int interval, Output output, Callback complete) {
30 | this.address = address;
31 | this.count = count;
32 | this.size = size;
33 | this.interval = interval;
34 | this.output = output;
35 | this.complete = complete;
36 | this.stopped = false;
37 | }
38 |
39 | public static Task start(String address, Output output, Callback complete) {
40 | return start(address, 10, output, complete);
41 | }
42 |
43 | public static Task start(String address, int count,
44 | Output output, Callback complete) {
45 | final Ping p = new Ping(address, count, output, complete);
46 | Util.runInBack(new Runnable() {
47 | @Override
48 | public void run() {
49 | p.run();
50 | }
51 | });
52 | return p;
53 | }
54 |
55 | private static String getIp(String host) throws UnknownHostException {
56 | InetAddress i = InetAddress.getByName(host);
57 | return i.getHostAddress();
58 | }
59 |
60 | private void run() {
61 | Ping.Result r = pingCmd();
62 | complete.complete(r);
63 | }
64 |
65 | private Ping.Result pingCmd() {
66 | String ip;
67 | try {
68 | ip = getIp(address);
69 | } catch (UnknownHostException e) {
70 | e.printStackTrace();
71 | return new Result("", "", 0, 0);
72 | }
73 | String cmd = String.format(Locale.getDefault(), "ping -n -i %f -s %d -c %d %s", ((double) interval / 1000), size, count, ip);
74 | Process process = null;
75 | StringBuilder str = new StringBuilder();
76 | BufferedReader reader = null;
77 | BufferedReader errorReader = null;
78 | try {
79 | process = Runtime.getRuntime().exec(cmd);
80 | reader = new BufferedReader(new InputStreamReader(
81 | process.getInputStream()));
82 | String line;
83 | errorReader = new BufferedReader(new InputStreamReader(
84 | process.getErrorStream()));
85 | while ((line = reader.readLine()) != null) {
86 | str.append(line).append("\n");
87 | output.write(line);
88 | }
89 | while ((line = errorReader.readLine()) != null) {
90 | str.append(line);
91 | output.write(line);
92 | }
93 | reader.close();
94 | errorReader.close();
95 | process.waitFor();
96 |
97 | } catch (IOException e) {
98 | e.printStackTrace();
99 | } catch (InterruptedException e) {
100 | e.printStackTrace();
101 | } finally {
102 | try {
103 | if (reader != null) {
104 | reader.close();
105 | }
106 | if (process != null) {
107 | process.destroy();
108 | }
109 | } catch (Exception e) {
110 | e.printStackTrace();
111 | }
112 | }
113 | return new Result(str.toString(), ip, size, interval);
114 | }
115 |
116 | @Override
117 | public void stop() {
118 | stopped = true;
119 | }
120 |
121 | public interface Callback {
122 | void complete(Result r);
123 | }
124 |
125 | public static class Result {
126 | public final String result;
127 | public final String ip;
128 | public final int size;
129 | public final int interval;
130 | private final String lastLinePrefix = "rtt min/avg/max/mdev = ";
131 | private final String packetWords = " packets transmitted";
132 | private final String receivedWords = " received";
133 | public int sent;
134 | public int dropped;
135 | public float max;
136 | public float min;
137 | public float avg;
138 | public float stddev;
139 | public int count;
140 |
141 | Result(String result, String ip, int size, int interval) {
142 | this.result = result;
143 | this.ip = ip;
144 | this.size = size;
145 | this.interval = interval;
146 | parseResult();
147 | }
148 |
149 | static String trimNoneDigital(String s) {
150 | if (s == null || s.length() == 0) {
151 | return "";
152 | }
153 | char[] v = s.toCharArray();
154 | char[] v2 = new char[v.length];
155 | int j = 0;
156 | for (char aV : v) {
157 | if ((aV >= '0' && aV <= '9') || aV == '.') {
158 | v2[j++] = aV;
159 | }
160 | }
161 | return new String(v2, 0, j);
162 | }
163 |
164 | private void parseRttLine(String s) {
165 | String s2 = s.substring(lastLinePrefix.length(), s.length() - 3);
166 | String[] l = s2.split("/");
167 | if (l.length != 4) {
168 | return;
169 | }
170 | min = Float.parseFloat(trimNoneDigital(l[0]));
171 | avg = Float.parseFloat(trimNoneDigital(l[1]));
172 | max = Float.parseFloat(trimNoneDigital(l[2]));
173 | stddev = Float.parseFloat(trimNoneDigital(l[3]));
174 | }
175 |
176 | private void parsePacketLine(String s) {
177 | String[] l = s.split(",");
178 | if (l.length != 4) {
179 | return;
180 | }
181 | if (l[0].length() > packetWords.length()) {
182 | String s2 = l[0].substring(0, l[0].length() - packetWords.length());
183 | count = Integer.parseInt(s2);
184 | }
185 | if (l[1].length() > receivedWords.length()) {
186 | String s3 = l[1].substring(0, l[1].length() - receivedWords.length());
187 | sent = Integer.parseInt(s3.trim());
188 | }
189 | dropped = count - sent;
190 | }
191 |
192 | private void parseResult() {
193 | String[] rs = result.split("\n");
194 | try {
195 | for (String s : rs) {
196 | if (s.contains(packetWords)) {
197 | parsePacketLine(s);
198 | } else if (s.contains(lastLinePrefix)) {
199 | parseRttLine(s);
200 | }
201 | }
202 | } catch (Exception e) {
203 | e.printStackTrace();
204 | }
205 |
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/RtmpPing.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.OutputStream;
6 | import java.net.InetAddress;
7 | import java.net.InetSocketAddress;
8 | import java.net.Socket;
9 | import java.net.SocketTimeoutException;
10 | import java.net.UnknownHostException;
11 | import java.util.Locale;
12 | import java.util.Random;
13 |
14 | /**
15 | * Created by bailong on 16/2/24.
16 | */
17 | public final class RtmpPing implements Task {
18 | public static final int TimeOut = -3;
19 | public static final int NotReach = -2;
20 | public static final int UnkownHost = -4;
21 | public static final int HandshakeFail = -5;
22 | public static final int Stopped = -1;
23 |
24 | public static final int ServerVersionError = -20001;
25 | public static final int ServerSignatureError = -20002;
26 | public static final int ServerTimeError = -20003;
27 |
28 | public static final int RTMP_SIG_SIZE = 1536;
29 |
30 | private final String host;
31 | private final int port;
32 | private final int count;
33 | private final Callback complete;
34 | private boolean stopped;
35 | private Output output;
36 |
37 | public RtmpPing(String host, int port, int count, Output output, Callback complete) {
38 | this.host = host;
39 | this.port = port;
40 | this.count = count;
41 | this.complete = complete;
42 | this.output = output;
43 | }
44 |
45 | public static Task start(String host, Output output, Callback complete) {
46 | return start(host, 1935, 2, output, complete);
47 | }
48 |
49 | public static Task start(String host, int port, int count
50 | , Output output, Callback complete) {
51 | final RtmpPing t = new RtmpPing(host, port, count, output, complete);
52 | Util.runInBack(new Runnable() {
53 | @Override
54 | public void run() {
55 | t.run();
56 | }
57 | });
58 | return t;
59 | }
60 |
61 | private static int readAll(InputStream in, byte[] buffer, int offset, int size) throws IOException {
62 | int pos = 0;
63 | while (pos < size) {
64 | int ret = in.read(buffer, offset + pos, size - pos);
65 | if (ret < 0) {
66 | return pos;
67 | }
68 | pos += ret;
69 | }
70 | return pos;
71 | }
72 |
73 | private static void writeAll(OutputStream outputStream, byte[] buffer, int offset, int n) throws IOException {
74 | outputStream.write(buffer, offset, n);
75 | outputStream.flush();
76 | }
77 |
78 | private static byte[] c0_c1() throws IOException {
79 | byte[] data = new byte[RTMP_SIG_SIZE + 1];
80 | int i = 0;
81 | data[i++] = 0x03; /* not encrypted */
82 | //time
83 | for (; i < 5; i++) {
84 | data[i] = 0;
85 | }
86 | //zero
87 | for (; i < 9; i++) {
88 | data[i] = 0;
89 | }
90 |
91 | Random r = new Random();
92 | for (; i < data.length; i++) {
93 | data[i] = (byte) r.nextInt(256);
94 | }
95 | return data;
96 | }
97 |
98 | private static void send_c0_c1(OutputStream outputStream, byte[] c0_c1) throws IOException {
99 | writeAll(outputStream, c0_c1, 0, c0_c1.length);
100 | }
101 |
102 | private static void send_c2(OutputStream outputStream, byte[] c2, int offset) throws IOException {
103 | writeAll(outputStream, c2, offset, c2.length - offset);
104 | }
105 |
106 | private static boolean verify_s0_s1(InputStream inputStream, byte[] s0_s1) throws IOException {
107 | int r = readAll(inputStream, s0_s1, 0, s0_s1.length);
108 | if (r != s0_s1.length) {
109 | throw new IOException("read not complete, read " + r);
110 | }
111 | byte s0 = s0_s1[0];
112 |
113 | return s0 == 0x03;
114 | }
115 |
116 | private static boolean verify_s2(InputStream inputStream,
117 | byte[] server_sig, byte[] client_sig) throws IOException {
118 | int n = readAll(inputStream, server_sig, 1, server_sig.length - 1);
119 | if (n != server_sig.length - 1) {
120 | throw new IOException("read not complete");
121 | }
122 |
123 | for (int i = 1; i < server_sig.length; i++) {
124 | if (server_sig[i] != client_sig[i]) {
125 | return false;
126 | }
127 | }
128 | return true;
129 | }
130 |
131 | private void run() {
132 | InetAddress[] addrs = null;
133 | try {
134 | addrs = InetAddress.getAllByName(host);
135 | } catch (UnknownHostException e) {
136 | e.printStackTrace();
137 | output.write("Unknown host: " + host);
138 | Util.runInMain(new Runnable() {
139 | @Override
140 | public void run() {
141 | complete.complete(new Result(UnkownHost, "", 0, 0, 0, 0));
142 | }
143 | });
144 | return;
145 | }
146 |
147 | final String ip = addrs[0].getHostAddress();
148 | InetSocketAddress server = new InetSocketAddress(ip, port);
149 | output.write("connect to " + ip + ":" + port);
150 | int[] times = new int[count];
151 | int index = -1;
152 | for (int i = 0; i < count && !stopped; i++) {
153 | long start = System.currentTimeMillis();
154 | Socket sock;
155 | try {
156 | sock = connect(server, 20 * 1000);
157 | } catch (IOException e) {
158 | e.printStackTrace();
159 | int code = NotReach;
160 | if (e instanceof SocketTimeoutException) {
161 | code = TimeOut;
162 | }
163 | final int code2 = code;
164 | Util.runInMain(new Runnable() {
165 | @Override
166 | public void run() {
167 | complete.complete(new Result(code2, ip, 0, 0, 0, 0));
168 | }
169 | });
170 | return;
171 | }
172 |
173 | long connEnd = System.currentTimeMillis();
174 | int connect_time = (int) (connEnd - start);
175 |
176 | try {
177 | handshake(sock);
178 | } catch (IOException e) {
179 | e.printStackTrace();
180 | Util.runInMain(new Runnable() {
181 | @Override
182 | public void run() {
183 | complete.complete(new Result(HandshakeFail, ip, 0, 0, 0, 0));
184 | }
185 | });
186 | return;
187 | } finally {
188 | try {
189 | sock.close();
190 | } catch (IOException e) {
191 | e.printStackTrace();
192 | }
193 | }
194 | long end = System.currentTimeMillis();
195 | times[i] = (int) (end - start);
196 | index = i;
197 | output.write(String.format(Locale.getDefault(), "%d: conn:%d handshake:%d",
198 | index, connect_time, (int) (end - start)));
199 | try {
200 | if (!stopped && i != count - 1 && 100 > (end - start)) {
201 | Thread.sleep(100 - (end - start));
202 | }
203 | } catch (InterruptedException e) {
204 | e.printStackTrace();
205 | }
206 | }
207 | if (index == -1) {
208 | complete.complete(new Result(Stopped, ip, 0, 0, 0, 0));
209 | return;
210 | }
211 |
212 | complete.complete(buildResult(times, index, ip));
213 | }
214 |
215 | private Result buildResult(int[] times, int index, String ip) {
216 | int sum = 0;
217 | int min = 1000000;
218 | int max = 0;
219 | for (int i = 0; i <= index; i++) {
220 | int t = times[i];
221 | if (t > max) {
222 | max = t;
223 | }
224 | if (t < min) {
225 | min = t;
226 | }
227 | sum += t;
228 | }
229 | return new Result(0, ip, max, min, sum / (index + 1), index + 1);
230 | }
231 |
232 | private Socket connect(InetSocketAddress socketAddress, int timeOut) throws IOException {
233 | Socket socket = new Socket();
234 | socket.setTcpNoDelay(true);
235 | socket.setSoTimeout(30 * 1000);
236 |
237 | try {
238 | socket.connect(socketAddress, timeOut);
239 | } catch (Exception e) {
240 | socket.close();
241 | throw e;
242 | }
243 | return socket;
244 | }
245 |
246 | private int handshake(Socket socket) throws IOException {
247 | OutputStream out = socket.getOutputStream();
248 | InputStream input = socket.getInputStream();
249 |
250 | byte[] c0_c1 = c0_c1();
251 |
252 | send_c0_c1(out, c0_c1);
253 |
254 | byte[] s0_s1 = new byte[RTMP_SIG_SIZE + 1];
255 |
256 | boolean b = verify_s0_s1(input, s0_s1);
257 | if (!b) {
258 | return ServerVersionError;
259 | }
260 |
261 | send_c2(out, s0_s1, 1);
262 |
263 |
264 | b = verify_s2(input, s0_s1, c0_c1);
265 | if (!b) {
266 | return ServerSignatureError;
267 | }
268 | return 0;
269 | }
270 |
271 | @Override
272 | public void stop() {
273 | stopped = true;
274 | }
275 |
276 | public interface Callback {
277 | void complete(Result r);
278 | }
279 |
280 | public static final class Result {
281 | public final int code;
282 | public final String ip;
283 | public final int maxTime;
284 | public final int minTime;
285 | public final int avgTime;
286 | public final int count;
287 |
288 | public Result(int code, String ip, int maxTime, int minTime, int avgTime,
289 | int count) {
290 | this.code = code;
291 | this.ip = ip;
292 | this.maxTime = maxTime;
293 | this.minTime = minTime;
294 | this.avgTime = avgTime;
295 | this.count = count;
296 | }
297 | }
298 | }
299 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/Task.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | /**
4 | * Created by bailong on 16/2/24.
5 | */
6 | public interface Task {
7 | void stop();
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/TcpPing.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.IOException;
4 | import java.net.InetAddress;
5 | import java.net.InetSocketAddress;
6 | import java.net.Socket;
7 | import java.net.SocketTimeoutException;
8 | import java.net.UnknownHostException;
9 |
10 | /**
11 | * Created by bailong on 16/2/24.
12 | */
13 | public final class TcpPing implements Task {
14 |
15 | public static final int TimeOut = -3;
16 | public static final int NotReach = -2;
17 | public static final int UnkownHost = -4;
18 | public static final int Stopped = -1;
19 | private final String host;
20 | private final int port;
21 | private final int count;
22 | private final Callback complete;
23 | private boolean stopped;
24 | private Output output;
25 |
26 | private TcpPing(String host, int port, int count, Output output, Callback complete) {
27 | this.host = host;
28 | this.port = port;
29 | this.count = count;
30 | this.complete = complete;
31 | this.output = output;
32 | }
33 |
34 | public static Task start(String host, Output output, Callback complete) {
35 | return start(host, 80, 3, output, complete);
36 | }
37 |
38 | public static Task start(String host, int port, int count
39 | , Output output, Callback complete) {
40 | final TcpPing t = new TcpPing(host, port, count, output, complete);
41 | Util.runInBack(new Runnable() {
42 | @Override
43 | public void run() {
44 | t.run();
45 | }
46 | });
47 | return t;
48 | }
49 |
50 | private void run() {
51 | InetAddress[] addrs = null;
52 | try {
53 | addrs = InetAddress.getAllByName(host);
54 | } catch (UnknownHostException e) {
55 | e.printStackTrace();
56 | output.write("Unknown host: " + host);
57 | Util.runInMain(new Runnable() {
58 | @Override
59 | public void run() {
60 | complete.complete(new Result(UnkownHost, "", 0, 0, 0, 0, 0, 0));
61 | }
62 | });
63 | return;
64 | }
65 |
66 | final String ip = addrs[0].getHostAddress();
67 | InetSocketAddress server = new InetSocketAddress(ip, port);
68 | output.write("connect to " + ip + ":" + port);
69 | int[] times = new int[count];
70 | int index = -1;
71 | int dropped = 0;
72 | for (int i = 0; i < count && !stopped; i++) {
73 | long start = System.currentTimeMillis();
74 | try {
75 | connect(server, 20 * 1000);
76 | } catch (IOException e) {
77 | e.printStackTrace();
78 | output.write(e.getMessage());
79 | int code = NotReach;
80 | if (e instanceof SocketTimeoutException) {
81 | code = TimeOut;
82 | }
83 | final int code2 = code;
84 | if (i == 0) {
85 | Util.runInMain(new Runnable() {
86 | @Override
87 | public void run() {
88 | complete.complete(new Result(code2, ip, 0, 0, 0, 0, 1, 1));
89 | }
90 | });
91 | return;
92 | } else {
93 | dropped++;
94 | }
95 | }
96 | long end = System.currentTimeMillis();
97 | int t = (int) (end - start);
98 | times[i] = t;
99 | index = i;
100 | try {
101 | if (!stopped && 100 > t && t > 0) {
102 | Thread.sleep(100 - t);
103 | }
104 | } catch (Exception e) {
105 | e.printStackTrace();
106 | }
107 | }
108 | if (index == -1) {
109 | complete.complete(new Result(Stopped, ip, 0, 0, 0, 0, 0, 0));
110 | return;
111 | }
112 |
113 | complete.complete(buildResult(times, index, ip, dropped));
114 | }
115 |
116 | private Result buildResult(int[] times, int index, String ip, int dropped) {
117 | int sum = 0;
118 | int min = 1000000;
119 | int max = 0;
120 | for (int i = 0; i <= index; i++) {
121 | int t = times[i];
122 | if (t > max) {
123 | max = t;
124 | }
125 | if (t < min) {
126 | min = t;
127 | }
128 | sum += t;
129 | }
130 | return new Result(0, ip, max, min, sum / (index + 1), 0, index + 1, dropped);
131 | }
132 |
133 | private void connect(InetSocketAddress socketAddress, int timeOut) throws IOException {
134 | Socket socket = null;
135 | try {
136 | socket = new Socket();
137 | socket.connect(socketAddress, timeOut);
138 | } catch (IOException e) {
139 | e.printStackTrace();
140 | throw e;
141 | } finally {
142 | if (socket != null) {
143 | try {
144 | socket.close();
145 | } catch (IOException e) {
146 | e.printStackTrace();
147 | }
148 | }
149 | }
150 | }
151 |
152 | @Override
153 | public void stop() {
154 | stopped = true;
155 | }
156 |
157 | public interface Callback {
158 | void complete(Result r);
159 | }
160 |
161 | public static final class Result {
162 | public final int code;
163 | public final String ip;
164 | public final int maxTime;
165 | public final int minTime;
166 | public final int avgTime;
167 | public final int stddevTime;
168 | public final int count;
169 | public final int dropped;
170 |
171 | public Result(int code, String ip, int maxTime, int minTime, int avgTime,
172 | int stddevTime, int count, int dropped) {
173 | this.code = code;
174 | this.ip = ip;
175 | this.maxTime = maxTime;
176 | this.minTime = minTime;
177 | this.avgTime = avgTime;
178 | this.stddevTime = stddevTime;
179 | this.count = count;
180 | this.dropped = dropped;
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/TraceRoute.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.net.InetAddress;
7 | import java.net.UnknownHostException;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import static java.lang.Runtime.getRuntime;
12 |
13 | /**
14 | * Created by bailong on 16/2/24.
15 | */
16 | public final class TraceRoute implements Task {
17 | private static final int MaxHop = 31;
18 | private static final String Error = "network error";
19 | private static final String MATCH_TRACE_IP = "(?<=From )(?:[0-9]{1,3}\\.){3}[0-9]{1,3}";
20 | private static final String MATCH_PING_IP = "(?<=from ).*(?=: icmp_seq=1 ttl=)";
21 | private static final String MATCH_PING_TIME = "(?<=time=).*?ms";
22 | private final String address;
23 | private final Output output;
24 | private final Callback complete;
25 | private volatile boolean stopped = false;
26 | private Result result = null;
27 |
28 | private TraceRoute(String address, Output output, Callback complete) {
29 | this.address = address;
30 | this.output = output;
31 | this.complete = complete;
32 | }
33 |
34 | static Matcher traceMatcher(String str) {
35 | Pattern patternTrace = Pattern.compile(MATCH_TRACE_IP);
36 | return patternTrace.matcher(str);
37 | }
38 |
39 | static Matcher timeMatcher(String str) {
40 | Pattern patternTime = Pattern.compile(MATCH_PING_TIME);
41 | return patternTime.matcher(str);
42 | }
43 |
44 | static Matcher ipMatcher(String str) {
45 | Pattern patternIp = Pattern.compile(MATCH_PING_IP);
46 | return patternIp.matcher(str);
47 | }
48 |
49 | static String getIpFromTraceMatcher(Matcher m) {
50 | String pingIp = m.group();
51 | int start = pingIp.indexOf('(');
52 | if (start >= 0) {
53 | pingIp = pingIp.substring(start + 1);
54 | }
55 | return pingIp;
56 | }
57 |
58 | public static Task start(String address, Output output, Callback complete) {
59 | final TraceRoute t = new TraceRoute(address, output, complete);
60 | new Thread(new Runnable() {
61 | @Override
62 | public void run() {
63 | t.run();
64 | }
65 | }).start();
66 | // Util.runInBack(new Runnable() {
67 | // @Override
68 | // public void run() {
69 | // t.run();
70 | // }
71 | // });
72 | return t;
73 | }
74 |
75 | private static String getIp(String host) throws UnknownHostException {
76 | InetAddress i = InetAddress.getByName(host);
77 | return i.getHostAddress();
78 | }
79 |
80 | @Override
81 | public void stop() {
82 | stopped = true;
83 | }
84 |
85 | private Process executePingCmd(String host, int hop) throws IOException {
86 | String command = "ping -n -c 1 -t " + hop + " " + host;
87 | // System.out.println("cmd> " + command);
88 | return getRuntime().exec(command);
89 | }
90 |
91 | private String getPingtOutput(Process process) {
92 | BufferedReader reader = new BufferedReader(new InputStreamReader(
93 | process.getInputStream()));
94 | String line;
95 | StringBuilder text = new StringBuilder();
96 | try {
97 | while ((line = reader.readLine()) != null) {
98 | text.append(line);
99 | }
100 | } catch (IOException e) {
101 | e.printStackTrace();
102 | } finally {
103 | try {
104 | reader.close();
105 | } catch (IOException e) {
106 | e.printStackTrace();
107 | }
108 | }
109 |
110 | try {
111 | process.waitFor();
112 | } catch (InterruptedException e) {
113 | e.printStackTrace();
114 | }
115 | process.destroy();
116 | return text.toString();
117 | }
118 |
119 | private void printNormal(Matcher m, long time, StringBuilder lineBuffer) {
120 | String pingIp = getIpFromTraceMatcher(m);
121 | lineBuffer.append("\t");
122 | lineBuffer.append(pingIp);
123 | lineBuffer.append("\t\t");
124 | lineBuffer.append(time); // 近似值
125 | lineBuffer.append("ms\t");
126 |
127 | if (output != null) {
128 | output.write(lineBuffer.toString());
129 | }
130 | result.append(lineBuffer.toString());
131 | }
132 |
133 | private void printEnd(Matcher m, String out, StringBuilder lineBuffer) {
134 | String pingIp = m.group();
135 | Matcher matcherTime = timeMatcher(out);
136 | if (matcherTime.find()) {
137 | String time = matcherTime.group();
138 | lineBuffer.append("\t\t");
139 | lineBuffer.append(pingIp);
140 | lineBuffer.append("\t\t");
141 | lineBuffer.append(time);
142 | lineBuffer.append("\t");
143 | updateOut(lineBuffer.toString());
144 | }
145 | }
146 |
147 | private void updateOut(String str) {
148 | if (str != null) {
149 | output.write(str);
150 | }
151 | result.append(str);
152 | }
153 |
154 | private void run() {
155 | int hop = 1;
156 | String ip = null;
157 | try {
158 | ip = getIp(this.address);
159 | } catch (UnknownHostException e) {
160 | e.printStackTrace();
161 | updateOut("unknown host " + this.address);
162 | result = new Result("");
163 | this.complete.complete(result);
164 | return;
165 | }
166 |
167 | result = new Result(ip);
168 | Process p;
169 | while (hop < MaxHop && !stopped) {
170 | long t1 = System.currentTimeMillis();
171 | try {
172 | p = executePingCmd(ip, hop);
173 | } catch (IOException e) {
174 | e.printStackTrace();
175 | updateOut("ping cmd error " + e.getMessage());
176 | break;
177 | }
178 | long t2 = System.currentTimeMillis();
179 | String str = getPingtOutput(p);
180 | // System.out.println(">" + hop + " " + str.trim());
181 | if (str.length() == 0) {
182 | updateOut(Error);
183 | break;
184 | }
185 | Matcher m = traceMatcher(str);
186 |
187 | StringBuilder lineBuffer = new StringBuilder(256);
188 | lineBuffer.append(hop).append(".");
189 | if (m.find()) {
190 | printNormal(m, (t2 - t1) / 2, lineBuffer);
191 | } else {
192 | Matcher matchPingIp = ipMatcher(str);
193 | if (matchPingIp.find()) {
194 | printEnd(matchPingIp, str, lineBuffer);
195 | break;
196 | } else {
197 | lineBuffer.append("\t\t * \t");
198 | updateOut(lineBuffer.toString());
199 | }
200 | }
201 | hop++;
202 | }
203 | this.complete.complete(result);
204 | }
205 |
206 | public interface Callback {
207 | void complete(Result r);
208 | }
209 |
210 | public static class Result {
211 | public final String ip;
212 | private final StringBuilder builder = new StringBuilder();
213 | private String allData;
214 |
215 | public Result(String ip) {
216 | this.ip = ip;
217 | }
218 |
219 | public String content() {
220 | if (allData != null) {
221 | return allData;
222 | }
223 | allData = builder.toString();
224 | return allData;
225 | }
226 |
227 | private void append(String str) {
228 | builder.append(str);
229 | }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/Util.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.Build;
5 | import android.os.Handler;
6 | import android.os.Looper;
7 |
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.net.HttpURLConnection;
11 | import java.net.URL;
12 |
13 | /**
14 | * Created by bailong on 16/2/24.
15 | */
16 | public final class Util {
17 | private static final int Max = 64 * 1024;
18 |
19 | static String httpGetString(String url) throws IOException {
20 | byte[] b = httpGet(url);
21 | if (b == null) {
22 | return null;
23 | }
24 | return new String(b);
25 | }
26 |
27 | static byte[] httpGet(String url) throws IOException {
28 | URL u = new URL(url);
29 | HttpURLConnection httpConn = (HttpURLConnection) u.openConnection();
30 | httpConn.setConnectTimeout(10000);
31 | httpConn.setReadTimeout(20000);
32 | int responseCode = httpConn.getResponseCode();
33 | if (responseCode != HttpURLConnection.HTTP_OK) {
34 | return null;
35 | }
36 |
37 | int length = httpConn.getContentLength();
38 | if (length < 0) {
39 | length = Max;
40 | }
41 | if (length > Max) {
42 | return null;
43 | }
44 | InputStream is = httpConn.getInputStream();
45 | byte[] data = new byte[Max];
46 | int read = is.read(data);
47 | is.close();
48 | if (read <= 0) {
49 | return null;
50 | }
51 | if (read < data.length) {
52 | byte[] b = new byte[read];
53 | System.arraycopy(data, 0, b, 0, read);
54 | return b;
55 | }
56 | return data;
57 | }
58 |
59 | static void runInMain(Runnable r) {
60 | Handler h = new Handler(Looper.getMainLooper());
61 | h.post(r);
62 | }
63 |
64 | static void runInBack(Runnable r) {
65 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
66 | AsyncTask.execute(r);
67 | } else {
68 | new Thread(r).start();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/Version.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag;
2 |
3 | public interface Version {
4 | String VERSION = "0.1.1";
5 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/localdns/DnsException.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag.localdns;
2 |
3 | import java.io.IOException;
4 |
5 | /**
6 | * Created by bailong on 15/6/19.
7 | */
8 | public class DnsException extends IOException {
9 | public DnsException(String domain, String message) {
10 | super(domain + ": " + message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/localdns/DnsMessage.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag.localdns;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.DataInputStream;
6 | import java.io.DataOutputStream;
7 | import java.io.IOException;
8 | import java.io.OutputStream;
9 | import java.net.IDN;
10 | import java.net.InetAddress;
11 | import java.net.UnknownHostException;
12 | import java.util.HashSet;
13 |
14 | /**
15 | * reference github/rtreffer/minidns.
16 | */
17 | final class DnsMessage {
18 |
19 | public static byte[] buildQuery(String domain, int id) {
20 | ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
21 | DataOutputStream dos = new DataOutputStream(baos);
22 | int bits = 0;
23 | // recursionDesired
24 | bits |= (1 << 8);
25 |
26 | try {
27 | dos.writeShort((short) id);
28 | dos.writeShort((short) bits);
29 |
30 | // questions count
31 | dos.writeShort(1);
32 |
33 | // no answer
34 | dos.writeShort(0);
35 |
36 | // no nameserverRecords
37 | dos.writeShort(0);
38 |
39 | // no additionalResourceRecords
40 | dos.writeShort(0);
41 |
42 | dos.flush();
43 | writeQuestion(baos, domain);
44 | } catch (IOException e) {
45 | throw new AssertionError(e);
46 | }
47 |
48 | return baos.toByteArray();
49 | }
50 |
51 | private static void writeDomain(OutputStream out, String domain) throws IOException {
52 | for (String s : domain.split("[.\u3002\uFF0E\uFF61]")) {
53 | byte[] buffer = IDN.toASCII(s).getBytes();
54 | out.write(buffer.length);
55 | out.write(buffer, 0, buffer.length); // ?
56 | }
57 | out.write(0);
58 | }
59 |
60 | private static void writeQuestion(OutputStream out, String domain) throws IOException {
61 | DataOutputStream dos = new DataOutputStream(out);
62 | writeDomain(out, domain);
63 | // type A
64 | dos.writeShort(1);
65 | // class internet
66 | dos.writeShort(1);
67 | }
68 |
69 | public static Record[] parseResponse(byte[] response, int id, String domain) throws IOException {
70 | ByteArrayInputStream bis = new ByteArrayInputStream(response);
71 | DataInputStream dis = new DataInputStream(bis);
72 | int answerId = dis.readUnsignedShort();
73 | if (answerId != id) {
74 | throw new DnsException(domain, "the answer id " + answerId + " is not match " + id);
75 | }
76 | int header = dis.readUnsignedShort();
77 | boolean recursionDesired = ((header >> 8) & 1) == 1;
78 | boolean recursionAvailable = ((header >> 7) & 1) == 1;
79 | if (!(recursionAvailable && recursionDesired)) {
80 | throw new DnsException(domain, "the dns server cant support recursion ");
81 |
82 | }
83 |
84 | int questionCount = dis.readUnsignedShort();
85 | int answerCount = dis.readUnsignedShort();
86 | // nameserver Count
87 | dis.readUnsignedShort();
88 | // additionalResourceRecordCount
89 | dis.readUnsignedShort();
90 |
91 | // ignore questions
92 | readQuestions(dis, response, questionCount);
93 |
94 | return readAnswers(dis, response, answerCount);
95 | // ignore auth
96 | // ignore additional
97 | }
98 |
99 | /**
100 | * Parse a domain name starting at the current offset and moving the input
101 | * stream pointer past this domain name (even if cross references occure).
102 | *
103 | * @param dis The input stream.
104 | * @param data The raw data (for cross references).
105 | * @return The domain name string.
106 | * @throws IOException Should never happen.
107 | */
108 | private static String readName(DataInputStream dis, byte data[])
109 | throws IOException {
110 | int c = dis.readUnsignedByte();
111 | if ((c & 0xc0) == 0xc0) {
112 | c = ((c & 0x3f) << 8) + dis.readUnsignedByte();
113 | HashSet jumps = new HashSet();
114 | jumps.add(c);
115 | return readName(data, c, jumps);
116 | }
117 | if (c == 0) {
118 | return "";
119 | }
120 | byte b[] = new byte[c];
121 | dis.readFully(b);
122 | String s = IDN.toUnicode(new String(b));
123 | String t = readName(dis, data);
124 | if (t.length() > 0) {
125 | s = s + "." + t;
126 | }
127 | return s;
128 | }
129 |
130 | /**
131 | * Parse a domain name starting at the given offset.
132 | *
133 | * @param data The raw data.
134 | * @param offset The offset.
135 | * @param jumps The list of jumps (by now).
136 | * @return The parsed domain name.
137 | * @throws IOException on cycles.
138 | */
139 | private static String readName(
140 | byte data[],
141 | int offset,
142 | HashSet jumps
143 | ) throws IOException {
144 | int c = data[offset] & 0xff;
145 | if ((c & 0xc0) == 0xc0) {
146 | c = ((c & 0x3f) << 8) + (data[offset + 1] & 0xff);
147 | if (jumps.contains(c)) {
148 | throw new DnsException("", "Cyclic offsets detected.");
149 | }
150 | jumps.add(c);
151 | return readName(data, c, jumps);
152 | }
153 | if (c == 0) {
154 | return "";
155 | }
156 | String s = new String(data, offset + 1, c);
157 | String t = readName(data, offset + 1 + c, jumps);
158 | if (t.length() > 0) {
159 | s = s + "." + t;
160 | }
161 | return s;
162 | }
163 |
164 | private static void readQuestions(DataInputStream dis, byte[] data, int count) throws IOException {
165 | while (count-- > 0) {
166 | readName(dis, data);
167 | // type
168 | dis.readUnsignedShort();
169 | // class
170 | dis.readUnsignedShort();
171 | }
172 | }
173 |
174 | private static Record[] readAnswers(DataInputStream dis, byte[] data, int count) throws IOException {
175 | int offset = 0;
176 | Record[] ret = new Record[count];
177 | while (count-- > 0) {
178 | ret[offset++] = readRecord(dis, data);
179 | }
180 | return ret;
181 | }
182 |
183 | private static Record readRecord(DataInputStream dis, byte[] data) throws IOException {
184 | readName(dis, data);
185 | int type = dis.readUnsignedShort();
186 | // class
187 | dis.readUnsignedShort();
188 |
189 | long ttl = (((long) dis.readUnsignedShort()) << 16) +
190 | dis.readUnsignedShort();
191 | int payloadLength = dis.readUnsignedShort();
192 | String payload = null;
193 | switch (type) {
194 | case Record.TYPE_A:
195 | byte[] ip = new byte[4];
196 | dis.readFully(ip);
197 | payload = InetAddress.getByAddress(ip).getHostAddress();
198 | break;
199 | case Record.TYPE_CNAME:
200 | payload = readName(dis, data);
201 | break;
202 | default:
203 | payload = null;
204 | for (int i = 0; i < payloadLength; i++) {
205 | dis.readByte();
206 | }
207 | break;
208 | }
209 | if (payload == null) {
210 | throw new UnknownHostException("no record");
211 | }
212 | return new Record(payload, type, (int) ttl, System.currentTimeMillis() / 1000);
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/localdns/Record.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag.localdns;
2 |
3 | import java.util.Locale;
4 |
5 | /**
6 | * Created by bailong on 15/6/12.
7 | */
8 | public final class Record {
9 | /**
10 | * A 记录 类型
11 | */
12 | public static final int TYPE_A = 1;
13 |
14 | /**
15 | * CName 类型
16 | */
17 | public static final int TYPE_CNAME = 5;
18 |
19 | /**
20 | * 具体的值,A 记录时为IP,Cname时为指向的域名
21 | */
22 | public final String value;
23 |
24 | /**
25 | * 记录类型,A或者CName
26 | */
27 | public final int type;
28 |
29 | /**
30 | * TTL dns结果缓存时间
31 | */
32 | public final int ttl;
33 |
34 | /**
35 | * 时间戳,用来判断超时
36 | */
37 | public final long timeStamp;
38 |
39 | public Record(String value, int type, int ttl, long timeStamp) {
40 | this.value = value;
41 | this.type = type;
42 | this.ttl = ttl;
43 | this.timeStamp = timeStamp;
44 | }
45 |
46 | public boolean equals(Object o) {
47 | if (this == o) {
48 | return true;
49 | }
50 | if (o == null || !(o instanceof Record)) {
51 | return false;
52 | }
53 | Record another = (Record) o;
54 | return this.value.equals(another.value)
55 | && this.type == another.type
56 | && this.ttl == another.ttl
57 | && this.timeStamp == another.timeStamp;
58 | }
59 |
60 | public boolean isA() {
61 | return type == TYPE_A;
62 | }
63 |
64 |
65 | public String toString() {
66 | String t;
67 | if (type == TYPE_A) {
68 | t = "A";
69 | } else if (type == TYPE_CNAME) {
70 | t = "CNAME";
71 | } else {
72 | t = "type-" + type;
73 | }
74 |
75 | return String.format(Locale.ENGLISH, "%s\t%s\t%d", t, value, ttl);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/qiniu/android/netdiag/localdns/Resolver.java:
--------------------------------------------------------------------------------
1 | package com.qiniu.android.netdiag.localdns;
2 |
3 | import java.io.IOException;
4 | import java.net.DatagramPacket;
5 | import java.net.DatagramSocket;
6 | import java.net.InetAddress;
7 | import java.util.Random;
8 |
9 | /**
10 | * Created by bailong on 15/6/16.
11 | */
12 | public final class Resolver {
13 | private static final Random random = new Random();
14 |
15 | final InetAddress address;
16 |
17 | public Resolver(InetAddress address) {
18 | this.address = address;
19 | }
20 |
21 | public Record[] resolve(String domain) throws IOException {
22 | int id;
23 | synchronized (random) {
24 | id = random.nextInt() & 0XFF;
25 | }
26 | byte[] query = DnsMessage.buildQuery(domain, id);
27 | byte[] answer = udpCommunicate(query);
28 | if (answer == null) {
29 | throw new DnsException(domain, "cant get answer");
30 | }
31 |
32 | return DnsMessage.parseResponse(answer, id, domain);
33 | }
34 |
35 | private byte[] udpCommunicate(byte[] question) throws IOException {
36 | DatagramSocket socket = null;
37 | try {
38 | socket = new DatagramSocket();
39 | DatagramPacket packet = new DatagramPacket(question, question.length,
40 | address, 53);
41 | socket.setSoTimeout(10000);
42 | socket.send(packet);
43 | packet = new DatagramPacket(new byte[1500], 1500);
44 | socket.receive(packet);
45 |
46 | return packet.getData();
47 | } finally {
48 | if (socket != null) {
49 | socket.close();
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NetDiag
3 |
4 |
--------------------------------------------------------------------------------
/mvn_push.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Chris Banes
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 | apply plugin: 'maven'
18 | apply plugin: 'signing'
19 |
20 | def isReleaseBuild() {
21 | return VERSION_NAME.contains("SNAPSHOT") == false
22 | }
23 |
24 | def getReleaseRepositoryUrl() {
25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
27 | }
28 |
29 | def getSnapshotRepositoryUrl() {
30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
31 | : "https://oss.sonatype.org/content/repositories/snapshots/"
32 | }
33 |
34 | def getRepositoryUsername() {
35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
36 | }
37 |
38 | def getRepositoryPassword() {
39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
40 | }
41 |
42 | afterEvaluate { project ->
43 | uploadArchives {
44 | repositories {
45 | mavenDeployer {
46 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
47 |
48 | pom.groupId = GROUP
49 | pom.artifactId = POM_ARTIFACT_ID
50 | pom.version = VERSION_NAME
51 |
52 | repository(url: getReleaseRepositoryUrl()) {
53 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
54 | }
55 | snapshotRepository(url: getSnapshotRepositoryUrl()) {
56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
57 | }
58 |
59 | pom.project {
60 | name POM_NAME
61 | packaging POM_PACKAGING
62 | description POM_DESCRIPTION
63 | url POM_URL
64 |
65 | scm {
66 | url POM_SCM_URL
67 | connection POM_SCM_CONNECTION
68 | developerConnection POM_SCM_DEV_CONNECTION
69 | }
70 |
71 | licenses {
72 | license {
73 | name POM_LICENCE_NAME
74 | url POM_LICENCE_URL
75 | distribution POM_LICENCE_DIST
76 | }
77 | }
78 |
79 | developers {
80 | developer {
81 | id POM_DEVELOPER_ID
82 | name POM_DEVELOPER_NAME
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | signing {
91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
92 | sign configurations.archives
93 | }
94 |
95 | task androidJavadocs(type: Javadoc) {
96 | source = android.sourceSets.main.java.srcDirs
97 | options.encoding = "UTF-8"
98 | options.charSet = 'UTF-8'
99 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
100 | }
101 |
102 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
103 | classifier = 'javadoc'
104 | from androidJavadocs.destinationDir
105 | }
106 |
107 | task androidSourcesJar(type: Jar) {
108 | classifier = 'sources'
109 | from android.sourceSets.main.java.sourceFiles
110 | }
111 |
112 | artifacts {
113 | archives androidSourcesJar
114 | archives androidJavadocsJar
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 |
--------------------------------------------------------------------------------