am 8b1cd803: am 35ce7783: am 39105d78: Merge "Metric for network time and getBackoffMultiplier()"

* commit '8b1cd803589ab332f02ce9eed7333de7f8bab0a1':
  Metric for network time and getBackoffMultiplier()
diff --git a/src/com/android/volley/DefaultRetryPolicy.java b/src/com/android/volley/DefaultRetryPolicy.java
index ce4f9e0..d8abab0 100644
--- a/src/com/android/volley/DefaultRetryPolicy.java
+++ b/src/com/android/volley/DefaultRetryPolicy.java
@@ -29,7 +29,7 @@
     /** The maximum number of attempts. */
     private final int mMaxNumRetries;
 
-    /** The backoff multiplier for for the policy. */
+    /** The backoff multiplier for the policy. */
     private final float mBackoffMultiplier;
 
     /** The default socket timeout in milliseconds */
@@ -77,6 +77,13 @@
     }
 
     /**
+     * Returns the backoff multiplier for the policy.
+     */
+    public float getBackoffMultiplier() {
+        return mBackoffMultiplier;
+    }
+
+    /**
      * Prepares for the next retry by applying a backoff to the timeout.
      * @param error The error code of the last attempt.
      */
diff --git a/src/com/android/volley/NetworkDispatcher.java b/src/com/android/volley/NetworkDispatcher.java
index 9c1c2e3..beb7861 100644
--- a/src/com/android/volley/NetworkDispatcher.java
+++ b/src/com/android/volley/NetworkDispatcher.java
@@ -20,6 +20,7 @@
 import android.net.TrafficStats;
 import android.os.Build;
 import android.os.Process;
+import android.os.SystemClock;
 
 import java.util.concurrent.BlockingQueue;
 
@@ -82,6 +83,7 @@
     public void run() {
         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
         while (true) {
+            long startTimeMs = SystemClock.elapsedRealtime();
             Request<?> request;
             try {
                 // Take a request from the queue.
@@ -132,10 +134,13 @@
                 request.markDelivered();
                 mDelivery.postResponse(request, response);
             } catch (VolleyError volleyError) {
+                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                 parseAndDeliverNetworkError(request, volleyError);
             } catch (Exception e) {
                 VolleyLog.e(e, "Unhandled exception %s", e.toString());
-                mDelivery.postError(request, new VolleyError(e));
+                VolleyError volleyError = new VolleyError(e);
+                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
+                mDelivery.postError(request, volleyError);
             }
         }
     }
diff --git a/src/com/android/volley/NetworkResponse.java b/src/com/android/volley/NetworkResponse.java
index 6a0b5c2..a787fa7 100644
--- a/src/com/android/volley/NetworkResponse.java
+++ b/src/com/android/volley/NetworkResponse.java
@@ -31,21 +31,28 @@
      * @param data Response body
      * @param headers Headers returned with this response, or null for none
      * @param notModified True if the server returned a 304 and the data was already in cache
+     * @param networkTimeMs Round-trip network time to receive network response
      */
     public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
-            boolean notModified) {
+            boolean notModified, long networkTimeMs) {
         this.statusCode = statusCode;
         this.data = data;
         this.headers = headers;
         this.notModified = notModified;
+        this.networkTimeMs = networkTimeMs;
+    }
+
+    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
+            boolean notModified) {
+        this(statusCode, data, headers, notModified, 0);
     }
 
     public NetworkResponse(byte[] data) {
-        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
+        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false, 0);
     }
 
     public NetworkResponse(byte[] data, Map<String, String> headers) {
-        this(HttpStatus.SC_OK, data, headers, false);
+        this(HttpStatus.SC_OK, data, headers, false, 0);
     }
 
     /** The HTTP status code. */
@@ -59,4 +66,8 @@
 
     /** True if the server returned a 304 (Not Modified). */
     public final boolean notModified;
-}
\ No newline at end of file
+
+    /** Network roundtrip time in milliseconds. */
+    public final long networkTimeMs;
+}
+
diff --git a/src/com/android/volley/ServerError.java b/src/com/android/volley/ServerError.java
index b29a6c6..70a9fd5 100644
--- a/src/com/android/volley/ServerError.java
+++ b/src/com/android/volley/ServerError.java
@@ -20,7 +20,7 @@
 import com.android.volley.VolleyError;
 
 /**
- * Indicates that the error responded with an error response.
+ * Indicates that the server responded with an error response.
  */
 @SuppressWarnings("serial")
 public class ServerError extends VolleyError {
diff --git a/src/com/android/volley/VolleyError.java b/src/com/android/volley/VolleyError.java
index 4f7b883..1471d40 100644
--- a/src/com/android/volley/VolleyError.java
+++ b/src/com/android/volley/VolleyError.java
@@ -22,6 +22,7 @@
 @SuppressWarnings("serial")
 public class VolleyError extends Exception {
     public final NetworkResponse networkResponse;
+    private long networkTimeMs;
 
     public VolleyError() {
         networkResponse = null;
@@ -45,4 +46,12 @@
         super(cause);
         networkResponse = null;
     }
+
+    /* package */ void setNetworkTimeMs(long networkTimeMs) {
+       this.networkTimeMs = networkTimeMs;
+    }
+
+    public long getNetworkTimeMs() {
+       return networkTimeMs;
+    }
 }
diff --git a/src/com/android/volley/toolbox/BasicNetwork.java b/src/com/android/volley/toolbox/BasicNetwork.java
index c76ef6a..bc1cfdb 100644
--- a/src/com/android/volley/toolbox/BasicNetwork.java
+++ b/src/com/android/volley/toolbox/BasicNetwork.java
@@ -104,7 +104,8 @@
                     Entry entry = request.getCacheEntry();
                     if (entry == null) {
                         return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
-                                responseHeaders, true);
+                                responseHeaders, true,
+                                SystemClock.elapsedRealtime() - requestStart);
                     }
 
                     // A HTTP 304 response does not have all header fields. We
@@ -113,7 +114,8 @@
                     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                     entry.responseHeaders.putAll(responseHeaders);
                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
-                            entry.responseHeaders, true);
+                            entry.responseHeaders, true,
+                            SystemClock.elapsedRealtime() - requestStart);
                 }
 
                 // Some responses such as 204s do not have content.  We must check.
@@ -132,7 +134,8 @@
                 if (statusCode < 200 || statusCode > 299) {
                     throw new IOException();
                 }
-                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
+                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
+                        SystemClock.elapsedRealtime() - requestStart);
             } catch (SocketTimeoutException e) {
                 attemptRetryOnException("socket", request, new TimeoutError());
             } catch (ConnectTimeoutException e) {
@@ -150,7 +153,7 @@
                 VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                 if (responseContents != null) {
                     networkResponse = new NetworkResponse(statusCode, responseContents,
-                            responseHeaders, false);
+                            responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                     if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                             statusCode == HttpStatus.SC_FORBIDDEN) {
                         attemptRetryOnException("auth",