diff --git a/.idea/.name b/.idea/.name
index 6bc0c98..e7ad264 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-TwitterCrashlytics
\ No newline at end of file
+VolleyExample
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 47095cb..6d9880d 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -3,21 +3,21 @@
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 6cbedb4..94a25f7 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,5 @@
-
\ No newline at end of file
diff --git a/app/app.iml b/app/app.iml
index e940034..e7d1ad8 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -76,33 +76,33 @@
+
+
-
-
+
-
-
+
-
-
+
-
+
+
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 6e4bc0e..45229bd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
- buildToolsVersion "23.0.1"
+ buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.twittercrashlytics"
@@ -24,6 +24,6 @@ dependencies {
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:cardview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'
- compile 'com.google.code.gson:gson:2.5'
- compile project(':volleyLib')
+ compile project(':volley')
+ compile 'com.google.code.gson:gson:2.6.2'
}
diff --git a/build.gradle b/build.gradle
index 1b7886d..cb98401 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
+ classpath 'com.android.tools.build:gradle:2.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b609b6b..b9bf59f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Oct 01 19:13:32 IST 2015
+#Thu Apr 14 06:41:17 IST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/settings.gradle b/settings.gradle
index 3e604e4..eae506c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1 @@
-include ':app'
-include ':volleyLib'
+include ':app',':volley'
diff --git a/volley/.gitignore b/volley/.gitignore
new file mode 100644
index 0000000..8889923
--- /dev/null
+++ b/volley/.gitignore
@@ -0,0 +1,9 @@
+bin
+gen
+.gradle
+build
+.settings
+target
+*.iml
+.idea
+local.properties
diff --git a/volley/Android.mk b/volley/Android.mk
new file mode 100644
index 0000000..dbe5194
--- /dev/null
+++ b/volley/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := volley
+LOCAL_SDK_VERSION := 17
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Include this library in the build server's output directory
+# TODO: Not yet.
+#$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):volley.jar)
+
+# Include build files in subdirectories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/volley/bintray.gradle b/volley/bintray.gradle
new file mode 100644
index 0000000..37a5e68
--- /dev/null
+++ b/volley/bintray.gradle
@@ -0,0 +1,87 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2"
+ }
+}
+
+// apply the plugin with its class name rather than its Id to work around gradle limitation of
+// not being able to find the plugin by Id despite the dependencies being added right above. Gradle
+// is currently not capable of loading plugins by Id if the dependency is anywhere else than
+// in the main project build.gradle. This file is "imported" into the project's build.gradle
+// through a "apply from:".
+apply plugin: com.jfrog.bintray.gradle.BintrayPlugin
+apply plugin: 'maven-publish'
+
+project.ext.group = 'com.android.volley'
+project.ext.archivesBaseName = 'volley'
+project.ext.version = '1.0.0'
+project.ext.pomDesc = 'Volley Android library'
+
+task sourcesJar(type: Jar) {
+ classifier = 'sources'
+ from android.sourceSets.main.java.srcDirs
+}
+
+task javadoc(type: Javadoc) {
+ source = android.sourceSets.main.java.srcDirs
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+}
+
+artifacts {
+ archives javadocJar
+ archives sourcesJar
+}
+
+publishing {
+ publications {
+ library(MavenPublication) {
+ groupId project.ext.group
+ artifactId project.ext.archivesBaseName
+ version project.ext.version
+
+ // Release AAR, Sources, and JavaDoc
+ artifact "$buildDir/outputs/aar/volley-release.aar"
+ artifact sourcesJar
+ artifact javadocJar
+ }
+ }
+}
+
+bintray {
+ user = System.env.BINTRAY_USER
+ key = System.env.BINTRAY_USER_KEY
+
+ publications = [ 'library' ]
+
+ publish = project.has("release")
+ pkg {
+ userOrg = 'android'
+ repo = 'android-utils'
+ group = project.ext.group
+ name = project.ext.group + '.' + project.ext.archivesBaseName
+ desc = project.ext.pomDesc
+ licenses = [ 'Apache-2.0' ]
+ websiteUrl = 'https://tools.android.com'
+ issueTrackerUrl = 'https://code.google.com/p/android/'
+ vcsUrl = 'https://android.googlesource.com/platform/frameworks/volley.git'
+ labels = ['android', 'volley', 'network']
+ publicDownloadNumbers = true
+
+ version {
+ name = project.ext.version
+ desc = project.ext.pomDesc + ' version ' + project.ext.version
+ gpg {
+ sign = true
+ passphrase = System.env.GPG_PASSPHRASE
+ }
+ }
+ }
+}
diff --git a/volley/build.gradle b/volley/build.gradle
new file mode 100644
index 0000000..677dabf
--- /dev/null
+++ b/volley/build.gradle
@@ -0,0 +1,38 @@
+// NOTE: The only changes that belong in this file are the definitions
+// of tool versions (gradle plugin, compile SDK, build tools), so that
+// Volley can be built via gradle as a standalone project.
+//
+// Any other changes to the build config belong in rules.gradle, which
+// is used by projects that depend on Volley but define their own
+// tools versions across all dependencies to ensure a consistent build.
+//
+// Most users should just add this line to settings.gradle:
+// include(":volley")
+//
+// If you have a more complicated Gradle setup you can choose to use
+// this instead:
+// include(":volley")
+// project(':volley').buildFileName = 'rules.gradle'
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.0.0'
+ }
+}
+
+apply plugin: 'com.android.library'
+
+repositories {
+ jcenter()
+}
+
+android {
+ compileSdkVersion 22
+ buildToolsVersion = '22.0.1'
+}
+
+apply from: 'rules.gradle'
+//apply from: 'bintray.gradle'
diff --git a/volley/build.xml b/volley/build.xml
new file mode 100644
index 0000000..219c63c
--- /dev/null
+++ b/volley/build.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/volley/custom_rules.xml b/volley/custom_rules.xml
new file mode 100644
index 0000000..1b94e5d
--- /dev/null
+++ b/volley/custom_rules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/volley/gradle/wrapper/gradle-wrapper.jar b/volley/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/volley/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/volley/gradle/wrapper/gradle-wrapper.properties b/volley/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..122a0dc
--- /dev/null
+++ b/volley/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/volley/gradlew b/volley/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/volley/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/volley/gradlew.bat b/volley/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/volley/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/volley/pom.xml b/volley/pom.xml
new file mode 100644
index 0000000..7c37e0f
--- /dev/null
+++ b/volley/pom.xml
@@ -0,0 +1,168 @@
+
+ 4.0.0
+
+ com.android.volley
+ volley
+ 1.0-SNAPSHOT
+ jar
+
+ volley
+ http://android.com
+
+
+ UTF-8
+
+ 1.6
+
+
+
+
+ com.google.android
+ android
+ 4.1.1.4
+
+
+ junit
+ junit
+ 4.10
+ test
+
+
+ org.robolectric
+ robolectric
+ 3.0
+ test
+
+
+ org.mockito
+ mockito-core
+ 1.9.5
+ test
+
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ 3.8.1
+
+
+ 19
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.0
+
+
+ ${java.version}
+
+
+
+
+
+
+
+
+ debug
+
+ true
+
+ performDebugBuild
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.18.1
+
+
+ default-test
+
+ ${surefireArgLine}
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+ 0.7.2.201409121644
+
+
+ pre-unit-test
+
+ prepare-agent
+
+
+ ${project.build.directory}/surefire-reports/jacoco-ut.exec
+ surefireArgLine
+
+
+
+ jacoco-report
+ post-integration-test
+
+ report
+ check
+
+
+ ${project.build.directory}/surefire-reports/jacoco-ut.exec
+ ${project.build.directory}/jacoco-report
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ 0.40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/volley/proguard-project.txt b/volley/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/volley/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/volley/proguard.cfg b/volley/proguard.cfg
new file mode 100644
index 0000000..b1cdf17
--- /dev/null
+++ b/volley/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native ;
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
diff --git a/volley/rules.gradle b/volley/rules.gradle
new file mode 100644
index 0000000..04dd681
--- /dev/null
+++ b/volley/rules.gradle
@@ -0,0 +1,12 @@
+// See build.gradle for an explanation of what this file is.
+
+apply plugin: 'com.android.library'
+
+// Check if the android plugin version supports unit testing.
+if (configurations.findByName("testCompile")) {
+ dependencies {
+ testCompile "junit:junit:4.10"
+ testCompile "org.mockito:mockito-core:1.9.5"
+ testCompile "org.robolectric:robolectric:3.0"
+ }
+}
diff --git a/volley/src/main/AndroidManifest.xml b/volley/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..16eec15
--- /dev/null
+++ b/volley/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/volley/src/main/java/com/android/volley/AuthFailureError.java b/volley/src/main/java/com/android/volley/AuthFailureError.java
new file mode 100644
index 0000000..87c811d
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/AuthFailureError.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.content.Intent;
+
+/**
+ * Error indicating that there was an authentication failure when performing a Request.
+ */
+@SuppressWarnings("serial")
+public class AuthFailureError extends VolleyError {
+ /** An intent that can be used to resolve this exception. (Brings up the password dialog.) */
+ private Intent mResolutionIntent;
+
+ public AuthFailureError() { }
+
+ public AuthFailureError(Intent intent) {
+ mResolutionIntent = intent;
+ }
+
+ public AuthFailureError(NetworkResponse response) {
+ super(response);
+ }
+
+ public AuthFailureError(String message) {
+ super(message);
+ }
+
+ public AuthFailureError(String message, Exception reason) {
+ super(message, reason);
+ }
+
+ public Intent getResolutionIntent() {
+ return mResolutionIntent;
+ }
+
+ @Override
+ public String getMessage() {
+ if (mResolutionIntent != null) {
+ return "User needs to (re)enter credentials.";
+ }
+ return super.getMessage();
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/Cache.java b/volley/src/main/java/com/android/volley/Cache.java
new file mode 100644
index 0000000..f1ec757
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/Cache.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An interface for a cache keyed by a String with a byte array as data.
+ */
+public interface Cache {
+ /**
+ * Retrieves an entry from the cache.
+ * @param key Cache key
+ * @return An {@link Entry} or null in the event of a cache miss
+ */
+ public Entry get(String key);
+
+ /**
+ * Adds or replaces an entry to the cache.
+ * @param key Cache key
+ * @param entry Data to store and metadata for cache coherency, TTL, etc.
+ */
+ public void put(String key, Entry entry);
+
+ /**
+ * Performs any potentially long-running actions needed to initialize the cache;
+ * will be called from a worker thread.
+ */
+ public void initialize();
+
+ /**
+ * Invalidates an entry in the cache.
+ * @param key Cache key
+ * @param fullExpire True to fully expire the entry, false to soft expire
+ */
+ public void invalidate(String key, boolean fullExpire);
+
+ /**
+ * Removes an entry from the cache.
+ * @param key Cache key
+ */
+ public void remove(String key);
+
+ /**
+ * Empties the cache.
+ */
+ public void clear();
+
+ /**
+ * Data and metadata for an entry returned by the cache.
+ */
+ public static class Entry {
+ /** The data returned from cache. */
+ public byte[] data;
+
+ /** ETag for cache coherency. */
+ public String etag;
+
+ /** Date of this response as reported by the server. */
+ public long serverDate;
+
+ /** The last modified date for the requested object. */
+ public long lastModified;
+
+ /** TTL for this record. */
+ public long ttl;
+
+ /** Soft TTL for this record. */
+ public long softTtl;
+
+ /** Immutable response headers as received from server; must be non-null. */
+ public Map responseHeaders = Collections.emptyMap();
+
+ /** True if the entry is expired. */
+ public boolean isExpired() {
+ return this.ttl < System.currentTimeMillis();
+ }
+
+ /** True if a refresh is needed from the original data source. */
+ public boolean refreshNeeded() {
+ return this.softTtl < System.currentTimeMillis();
+ }
+ }
+
+}
diff --git a/volley/src/main/java/com/android/volley/CacheDispatcher.java b/volley/src/main/java/com/android/volley/CacheDispatcher.java
new file mode 100644
index 0000000..18d219b
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/CacheDispatcher.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.os.Process;
+
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * Provides a thread for performing cache triage on a queue of requests.
+ *
+ * Requests added to the specified cache queue are resolved from cache.
+ * Any deliverable response is posted back to the caller via a
+ * {@link ResponseDelivery}. Cache misses and responses that require
+ * refresh are enqueued on the specified network queue for processing
+ * by a {@link NetworkDispatcher}.
+ */
+public class CacheDispatcher extends Thread {
+
+ private static final boolean DEBUG = VolleyLog.DEBUG;
+
+ /** The queue of requests coming in for triage. */
+ private final BlockingQueue> mCacheQueue;
+
+ /** The queue of requests going out to the network. */
+ private final BlockingQueue> mNetworkQueue;
+
+ /** The cache to read from. */
+ private final Cache mCache;
+
+ /** For posting responses. */
+ private final ResponseDelivery mDelivery;
+
+ /** Used for telling us to die. */
+ private volatile boolean mQuit = false;
+
+ /**
+ * Creates a new cache triage dispatcher thread. You must call {@link #start()}
+ * in order to begin processing.
+ *
+ * @param cacheQueue Queue of incoming requests for triage
+ * @param networkQueue Queue to post requests that require network to
+ * @param cache Cache interface to use for resolution
+ * @param delivery Delivery interface to use for posting responses
+ */
+ public CacheDispatcher(
+ BlockingQueue> cacheQueue, BlockingQueue> networkQueue,
+ Cache cache, ResponseDelivery delivery) {
+ mCacheQueue = cacheQueue;
+ mNetworkQueue = networkQueue;
+ mCache = cache;
+ mDelivery = delivery;
+ }
+
+ /**
+ * Forces this dispatcher to quit immediately. If any requests are still in
+ * the queue, they are not guaranteed to be processed.
+ */
+ public void quit() {
+ mQuit = true;
+ interrupt();
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) VolleyLog.v("start new dispatcher");
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+ // Make a blocking call to initialize the cache.
+ mCache.initialize();
+
+ while (true) {
+ try {
+ // Get a request from the cache triage queue, blocking until
+ // at least one is available.
+ final Request> request = mCacheQueue.take();
+ request.addMarker("cache-queue-take");
+
+ // If the request has been canceled, don't bother dispatching it.
+ if (request.isCanceled()) {
+ request.finish("cache-discard-canceled");
+ continue;
+ }
+
+ // Attempt to retrieve this item from cache.
+ Cache.Entry entry = mCache.get(request.getCacheKey());
+ if (entry == null) {
+ request.addMarker("cache-miss");
+ // Cache miss; send off to the network dispatcher.
+ mNetworkQueue.put(request);
+ continue;
+ }
+
+ // If it is completely expired, just send it to the network.
+ if (entry.isExpired()) {
+ request.addMarker("cache-hit-expired");
+ request.setCacheEntry(entry);
+ mNetworkQueue.put(request);
+ continue;
+ }
+
+ // We have a cache hit; parse its data for delivery back to the request.
+ request.addMarker("cache-hit");
+ Response> response = request.parseNetworkResponse(
+ new NetworkResponse(entry.data, entry.responseHeaders));
+ request.addMarker("cache-hit-parsed");
+
+ if (!entry.refreshNeeded()) {
+ // Completely unexpired cache hit. Just deliver the response.
+ mDelivery.postResponse(request, response);
+ } else {
+ // Soft-expired cache hit. We can deliver the cached response,
+ // but we need to also send the request to the network for
+ // refreshing.
+ request.addMarker("cache-hit-refresh-needed");
+ request.setCacheEntry(entry);
+
+ // Mark the response as intermediate.
+ response.intermediate = true;
+
+ // Post the intermediate response back to the user and have
+ // the delivery then forward the request along to the network.
+ mDelivery.postResponse(request, response, new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mNetworkQueue.put(request);
+ } catch (InterruptedException e) {
+ // Not much we can do about this.
+ }
+ }
+ });
+ }
+
+ } catch (InterruptedException e) {
+ // We may have been interrupted because it was time to quit.
+ if (mQuit) {
+ return;
+ }
+ continue;
+ }
+ }
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/ClientError.java b/volley/src/main/java/com/android/volley/ClientError.java
new file mode 100644
index 0000000..a8c8141
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/ClientError.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Indicates that the server responded with an error response indicating that the client has erred.
+ *
+ * For backwards compatibility, extends ServerError which used to be thrown for all server errors,
+ * including 4xx error codes indicating a client error.
+ */
+@SuppressWarnings("serial")
+public class ClientError extends ServerError {
+ public ClientError(NetworkResponse networkResponse) {
+ super(networkResponse);
+ }
+
+ public ClientError() {
+ super();
+ }
+}
+
diff --git a/volley/src/main/java/com/android/volley/DefaultRetryPolicy.java b/volley/src/main/java/com/android/volley/DefaultRetryPolicy.java
new file mode 100644
index 0000000..d8abab0
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/DefaultRetryPolicy.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Default retry policy for requests.
+ */
+public class DefaultRetryPolicy implements RetryPolicy {
+ /** The current timeout in milliseconds. */
+ private int mCurrentTimeoutMs;
+
+ /** The current retry count. */
+ private int mCurrentRetryCount;
+
+ /** The maximum number of attempts. */
+ private final int mMaxNumRetries;
+
+ /** The backoff multiplier for the policy. */
+ private final float mBackoffMultiplier;
+
+ /** The default socket timeout in milliseconds */
+ public static final int DEFAULT_TIMEOUT_MS = 2500;
+
+ /** The default number of retries */
+ public static final int DEFAULT_MAX_RETRIES = 1;
+
+ /** The default backoff multiplier */
+ public static final float DEFAULT_BACKOFF_MULT = 1f;
+
+ /**
+ * Constructs a new retry policy using the default timeouts.
+ */
+ public DefaultRetryPolicy() {
+ this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
+ }
+
+ /**
+ * Constructs a new retry policy.
+ * @param initialTimeoutMs The initial timeout for the policy.
+ * @param maxNumRetries The maximum number of retries.
+ * @param backoffMultiplier Backoff multiplier for the policy.
+ */
+ public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
+ mCurrentTimeoutMs = initialTimeoutMs;
+ mMaxNumRetries = maxNumRetries;
+ mBackoffMultiplier = backoffMultiplier;
+ }
+
+ /**
+ * Returns the current timeout.
+ */
+ @Override
+ public int getCurrentTimeout() {
+ return mCurrentTimeoutMs;
+ }
+
+ /**
+ * Returns the current retry count.
+ */
+ @Override
+ public int getCurrentRetryCount() {
+ return mCurrentRetryCount;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void retry(VolleyError error) throws VolleyError {
+ mCurrentRetryCount++;
+ mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
+ if (!hasAttemptRemaining()) {
+ throw error;
+ }
+ }
+
+ /**
+ * Returns true if this policy has attempts remaining, false otherwise.
+ */
+ protected boolean hasAttemptRemaining() {
+ return mCurrentRetryCount <= mMaxNumRetries;
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/ExecutorDelivery.java b/volley/src/main/java/com/android/volley/ExecutorDelivery.java
new file mode 100644
index 0000000..1babfcd
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/ExecutorDelivery.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.os.Handler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Delivers responses and errors.
+ */
+public class ExecutorDelivery implements ResponseDelivery {
+ /** Used for posting responses, typically to the main thread. */
+ private final Executor mResponsePoster;
+
+ /**
+ * Creates a new response delivery interface.
+ * @param handler {@link Handler} to post responses on
+ */
+ public ExecutorDelivery(final Handler handler) {
+ // Make an Executor that just wraps the handler.
+ mResponsePoster = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ handler.post(command);
+ }
+ };
+ }
+
+ /**
+ * Creates a new response delivery interface, mockable version
+ * for testing.
+ * @param executor For running delivery tasks
+ */
+ public ExecutorDelivery(Executor executor) {
+ mResponsePoster = executor;
+ }
+
+ @Override
+ public void postResponse(Request> request, Response> response) {
+ postResponse(request, response, null);
+ }
+
+ @Override
+ public void postResponse(Request> request, Response> response, Runnable runnable) {
+ request.markDelivered();
+ request.addMarker("post-response");
+ mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
+ }
+
+ @Override
+ public void postError(Request> request, VolleyError error) {
+ request.addMarker("post-error");
+ Response> response = Response.error(error);
+ mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
+ }
+
+ /**
+ * A Runnable used for delivering network responses to a listener on the
+ * main thread.
+ */
+ @SuppressWarnings("rawtypes")
+ private class ResponseDeliveryRunnable implements Runnable {
+ private final Request mRequest;
+ private final Response mResponse;
+ private final Runnable mRunnable;
+
+ public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
+ mRequest = request;
+ mResponse = response;
+ mRunnable = runnable;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ // If this request has canceled, finish it and don't deliver.
+ if (mRequest.isCanceled()) {
+ mRequest.finish("canceled-at-delivery");
+ return;
+ }
+
+ // Deliver a normal response or error, depending.
+ if (mResponse.isSuccess()) {
+ mRequest.deliverResponse(mResponse.result);
+ } else {
+ mRequest.deliverError(mResponse.error);
+ }
+
+ // If this is an intermediate response, add a marker, otherwise we're done
+ // and the request can be finished.
+ if (mResponse.intermediate) {
+ mRequest.addMarker("intermediate-response");
+ } else {
+ mRequest.finish("done");
+ }
+
+ // If we have been provided a post-delivery runnable, run it.
+ if (mRunnable != null) {
+ mRunnable.run();
+ }
+ }
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/Network.java b/volley/src/main/java/com/android/volley/Network.java
new file mode 100644
index 0000000..ab45830
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/Network.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * An interface for performing requests.
+ */
+public interface Network {
+ /**
+ * Performs the specified request.
+ * @param request Request to process
+ * @return A {@link NetworkResponse} with data and caching metadata; will never be null
+ * @throws VolleyError on errors
+ */
+ public NetworkResponse performRequest(Request> request) throws VolleyError;
+}
diff --git a/volley/src/main/java/com/android/volley/NetworkDispatcher.java b/volley/src/main/java/com/android/volley/NetworkDispatcher.java
new file mode 100644
index 0000000..beb7861
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/NetworkDispatcher.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.annotation.TargetApi;
+import android.net.TrafficStats;
+import android.os.Build;
+import android.os.Process;
+import android.os.SystemClock;
+
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * Provides a thread for performing network dispatch from a queue of requests.
+ *
+ * Requests added to the specified queue are processed from the network via a
+ * specified {@link Network} interface. Responses are committed to cache, if
+ * eligible, using a specified {@link Cache} interface. Valid responses and
+ * errors are posted back to the caller via a {@link ResponseDelivery}.
+ */
+public class NetworkDispatcher extends Thread {
+ /** The queue of requests to service. */
+ private final BlockingQueue> mQueue;
+ /** The network interface for processing requests. */
+ private final Network mNetwork;
+ /** The cache to write to. */
+ private final Cache mCache;
+ /** For posting responses and errors. */
+ private final ResponseDelivery mDelivery;
+ /** Used for telling us to die. */
+ private volatile boolean mQuit = false;
+
+ /**
+ * Creates a new network dispatcher thread. You must call {@link #start()}
+ * in order to begin processing.
+ *
+ * @param queue Queue of incoming requests for triage
+ * @param network Network interface to use for performing requests
+ * @param cache Cache interface to use for writing responses to cache
+ * @param delivery Delivery interface to use for posting responses
+ */
+ public NetworkDispatcher(BlockingQueue> queue,
+ Network network, Cache cache,
+ ResponseDelivery delivery) {
+ mQueue = queue;
+ mNetwork = network;
+ mCache = cache;
+ mDelivery = delivery;
+ }
+
+ /**
+ * Forces this dispatcher to quit immediately. If any requests are still in
+ * the queue, they are not guaranteed to be processed.
+ */
+ public void quit() {
+ mQuit = true;
+ interrupt();
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ private void addTrafficStatsTag(Request> request) {
+ // Tag the request (if API >= 14)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
+ }
+ }
+
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ while (true) {
+ long startTimeMs = SystemClock.elapsedRealtime();
+ Request> request;
+ try {
+ // Take a request from the queue.
+ request = mQueue.take();
+ } catch (InterruptedException e) {
+ // We may have been interrupted because it was time to quit.
+ if (mQuit) {
+ return;
+ }
+ continue;
+ }
+
+ try {
+ request.addMarker("network-queue-take");
+
+ // If the request was cancelled already, do not perform the
+ // network request.
+ if (request.isCanceled()) {
+ request.finish("network-discard-cancelled");
+ continue;
+ }
+
+ addTrafficStatsTag(request);
+
+ // Perform the network request.
+ NetworkResponse networkResponse = mNetwork.performRequest(request);
+ request.addMarker("network-http-complete");
+
+ // If the server returned 304 AND we delivered a response already,
+ // we're done -- don't deliver a second identical response.
+ if (networkResponse.notModified && request.hasHadResponseDelivered()) {
+ request.finish("not-modified");
+ continue;
+ }
+
+ // Parse the response here on the worker thread.
+ Response> response = request.parseNetworkResponse(networkResponse);
+ request.addMarker("network-parse-complete");
+
+ // Write to cache if applicable.
+ // TODO: Only update cache metadata instead of entire record for 304s.
+ if (request.shouldCache() && response.cacheEntry != null) {
+ mCache.put(request.getCacheKey(), response.cacheEntry);
+ request.addMarker("network-cache-written");
+ }
+
+ // Post the response back.
+ 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());
+ VolleyError volleyError = new VolleyError(e);
+ volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
+ mDelivery.postError(request, volleyError);
+ }
+ }
+ }
+
+ private void parseAndDeliverNetworkError(Request> request, VolleyError error) {
+ error = request.parseNetworkError(error);
+ mDelivery.postError(request, error);
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/NetworkError.java b/volley/src/main/java/com/android/volley/NetworkError.java
new file mode 100644
index 0000000..40b41c5
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/NetworkError.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Indicates that there was a network error when performing a Volley request.
+ */
+@SuppressWarnings("serial")
+public class NetworkError extends VolleyError {
+ public NetworkError() {
+ super();
+ }
+
+ public NetworkError(Throwable cause) {
+ super(cause);
+ }
+
+ public NetworkError(NetworkResponse networkResponse) {
+ super(networkResponse);
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/NetworkResponse.java b/volley/src/main/java/com/android/volley/NetworkResponse.java
new file mode 100644
index 0000000..a787fa7
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/NetworkResponse.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import org.apache.http.HttpStatus;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Data and headers returned from {@link Network#performRequest(Request)}.
+ */
+public class NetworkResponse {
+ /**
+ * Creates a new network response.
+ * @param statusCode the HTTP status code
+ * @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 headers,
+ 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 headers,
+ boolean notModified) {
+ this(statusCode, data, headers, notModified, 0);
+ }
+
+ public NetworkResponse(byte[] data) {
+ this(HttpStatus.SC_OK, data, Collections.emptyMap(), false, 0);
+ }
+
+ public NetworkResponse(byte[] data, Map headers) {
+ this(HttpStatus.SC_OK, data, headers, false, 0);
+ }
+
+ /** The HTTP status code. */
+ public final int statusCode;
+
+ /** Raw data from this response. */
+ public final byte[] data;
+
+ /** Response headers. */
+ public final Map headers;
+
+ /** True if the server returned a 304 (Not Modified). */
+ public final boolean notModified;
+
+ /** Network roundtrip time in milliseconds. */
+ public final long networkTimeMs;
+}
+
diff --git a/volley/src/main/java/com/android/volley/NoConnectionError.java b/volley/src/main/java/com/android/volley/NoConnectionError.java
new file mode 100644
index 0000000..fc23156
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/NoConnectionError.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Error indicating that no connection could be established when performing a Volley request.
+ */
+@SuppressWarnings("serial")
+public class NoConnectionError extends NetworkError {
+ public NoConnectionError() {
+ super();
+ }
+
+ public NoConnectionError(Throwable reason) {
+ super(reason);
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/ParseError.java b/volley/src/main/java/com/android/volley/ParseError.java
new file mode 100644
index 0000000..959d8fb
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/ParseError.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Indicates that the server's response could not be parsed.
+ */
+@SuppressWarnings("serial")
+public class ParseError extends VolleyError {
+ public ParseError() { }
+
+ public ParseError(NetworkResponse networkResponse) {
+ super(networkResponse);
+ }
+
+ public ParseError(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/Request.java b/volley/src/main/java/com/android/volley/Request.java
new file mode 100644
index 0000000..8200f6e
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/Request.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+
+import com.android.volley.VolleyLog.MarkerLog;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Base class for all network requests.
+ *
+ * @param The type of parsed response this request expects.
+ */
+public abstract class Request implements Comparable> {
+
+ /**
+ * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
+ */
+ private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
+
+ /**
+ * Supported request methods.
+ */
+ public interface Method {
+ int DEPRECATED_GET_OR_POST = -1;
+ int GET = 0;
+ int POST = 1;
+ int PUT = 2;
+ int DELETE = 3;
+ int HEAD = 4;
+ int OPTIONS = 5;
+ int TRACE = 6;
+ int PATCH = 7;
+ }
+
+ /** An event log tracing the lifetime of this request; for debugging. */
+ private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
+
+ /**
+ * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
+ * TRACE, and PATCH.
+ */
+ private final int mMethod;
+
+ /** URL of this request. */
+ private final String mUrl;
+
+ /** Default tag for {@link TrafficStats}. */
+ private final int mDefaultTrafficStatsTag;
+
+ /** Listener interface for errors. */
+ private final Response.ErrorListener mErrorListener;
+
+ /** Sequence number of this request, used to enforce FIFO ordering. */
+ private Integer mSequence;
+
+ /** The request queue this request is associated with. */
+ private RequestQueue mRequestQueue;
+
+ /** Whether or not responses to this request should be cached. */
+ private boolean mShouldCache = true;
+
+ /** Whether or not this request has been canceled. */
+ private boolean mCanceled = false;
+
+ /** Whether or not a response has been delivered for this request yet. */
+ private boolean mResponseDelivered = false;
+
+ /** Whether the request should be retried in the event of an HTTP 5xx (server) error. */
+ private boolean mShouldRetryServerErrors = false;
+
+ /** The retry policy for this request. */
+ private RetryPolicy mRetryPolicy;
+
+ /**
+ * When a request can be retrieved from cache but must be refreshed from
+ * the network, the cache entry will be stored here so that in the event of
+ * a "Not Modified" response, we can be sure it hasn't been evicted from cache.
+ */
+ private Cache.Entry mCacheEntry = null;
+
+ /** An opaque token tagging this request; used for bulk cancellation. */
+ private Object mTag;
+
+ /**
+ * Creates a new request with the given URL and error listener. Note that
+ * the normal response listener is not provided here as delivery of responses
+ * is provided by subclasses, who have a better idea of how to deliver an
+ * already-parsed response.
+ *
+ * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
+ */
+ @Deprecated
+ public Request(String url, Response.ErrorListener listener) {
+ this(Method.DEPRECATED_GET_OR_POST, url, listener);
+ }
+
+ /**
+ * Creates a new request with the given method (one of the values from {@link Method}),
+ * URL, and error listener. Note that the normal response listener is not provided here as
+ * delivery of responses is provided by subclasses, who have a better idea of how to deliver
+ * an already-parsed response.
+ */
+ public Request(int method, String url, Response.ErrorListener listener) {
+ mMethod = method;
+ mUrl = url;
+ mErrorListener = listener;
+ setRetryPolicy(new DefaultRetryPolicy());
+
+ mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
+ }
+
+ /**
+ * Return the method for this request. Can be one of the values in {@link Method}.
+ */
+ public int getMethod() {
+ return mMethod;
+ }
+
+ /**
+ * Set a tag on this request. Can be used to cancel all requests with this
+ * tag by {@link RequestQueue#cancelAll(Object)}.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public Request> setTag(Object tag) {
+ mTag = tag;
+ return this;
+ }
+
+ /**
+ * Returns this request's tag.
+ * @see Request#setTag(Object)
+ */
+ public Object getTag() {
+ return mTag;
+ }
+
+ /**
+ * @return this request's {@link com.android.volley.Response.ErrorListener}.
+ */
+ public Response.ErrorListener getErrorListener() {
+ return mErrorListener;
+ }
+
+ /**
+ * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
+ */
+ public int getTrafficStatsTag() {
+ return mDefaultTrafficStatsTag;
+ }
+
+ /**
+ * @return The hashcode of the URL's host component, or 0 if there is none.
+ */
+ private static int findDefaultTrafficStatsTag(String url) {
+ if (!TextUtils.isEmpty(url)) {
+ Uri uri = Uri.parse(url);
+ if (uri != null) {
+ String host = uri.getHost();
+ if (host != null) {
+ return host.hashCode();
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Sets the retry policy for this request.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public Request> setRetryPolicy(RetryPolicy retryPolicy) {
+ mRetryPolicy = retryPolicy;
+ return this;
+ }
+
+ /**
+ * Adds an event to this request's event log; for debugging.
+ */
+ public void addMarker(String tag) {
+ if (MarkerLog.ENABLED) {
+ mEventLog.add(tag, Thread.currentThread().getId());
+ }
+ }
+
+ /**
+ * Notifies the request queue that this request has finished (successfully or with error).
+ *
+ *
Also dumps all events from this request's event log; for debugging.
+ */
+ void finish(final String tag) {
+ if (mRequestQueue != null) {
+ mRequestQueue.finish(this);
+ }
+ if (MarkerLog.ENABLED) {
+ final long threadId = Thread.currentThread().getId();
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ // If we finish marking off of the main thread, we need to
+ // actually do it on the main thread to ensure correct ordering.
+ Handler mainThread = new Handler(Looper.getMainLooper());
+ mainThread.post(new Runnable() {
+ @Override
+ public void run() {
+ mEventLog.add(tag, threadId);
+ mEventLog.finish(this.toString());
+ }
+ });
+ return;
+ }
+
+ mEventLog.add(tag, threadId);
+ mEventLog.finish(this.toString());
+ }
+ }
+
+ /**
+ * Associates this request with the given queue. The request queue will be notified when this
+ * request has finished.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public Request> setRequestQueue(RequestQueue requestQueue) {
+ mRequestQueue = requestQueue;
+ return this;
+ }
+
+ /**
+ * Sets the sequence number of this request. Used by {@link RequestQueue}.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public final Request> setSequence(int sequence) {
+ mSequence = sequence;
+ return this;
+ }
+
+ /**
+ * Returns the sequence number of this request.
+ */
+ public final int getSequence() {
+ if (mSequence == null) {
+ throw new IllegalStateException("getSequence called before setSequence");
+ }
+ return mSequence;
+ }
+
+ /**
+ * Returns the URL of this request.
+ */
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Returns the cache key for this request. By default, this is the URL.
+ */
+ public String getCacheKey() {
+ return getUrl();
+ }
+
+ /**
+ * Annotates this request with an entry retrieved for it from cache.
+ * Used for cache coherency support.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public Request> setCacheEntry(Cache.Entry entry) {
+ mCacheEntry = entry;
+ return this;
+ }
+
+ /**
+ * Returns the annotated cache entry, or null if there isn't one.
+ */
+ public Cache.Entry getCacheEntry() {
+ return mCacheEntry;
+ }
+
+ /**
+ * Mark this request as canceled. No callback will be delivered.
+ */
+ public void cancel() {
+ mCanceled = true;
+ }
+
+ /**
+ * Returns true if this request has been canceled.
+ */
+ public boolean isCanceled() {
+ return mCanceled;
+ }
+
+ /**
+ * Returns a list of extra HTTP headers to go along with this request. Can
+ * throw {@link AuthFailureError} as authentication may be required to
+ * provide these values.
+ * @throws AuthFailureError In the event of auth failure
+ */
+ public Map getHeaders() throws AuthFailureError {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns a Map of POST parameters to be used for this request, or null if
+ * a simple GET should be used. Can throw {@link AuthFailureError} as
+ * authentication may be required to provide these values.
+ *
+ *
Note that only one of getPostParams() and getPostBody() can return a non-null
+ * value.
+ * @throws AuthFailureError In the event of auth failure
+ *
+ * @deprecated Use {@link #getParams()} instead.
+ */
+ @Deprecated
+ protected Map getPostParams() throws AuthFailureError {
+ return getParams();
+ }
+
+ /**
+ * Returns which encoding should be used when converting POST parameters returned by
+ * {@link #getPostParams()} into a raw POST body.
+ *
+ *
This controls both encodings:
+ *
+ *
The string encoding used when converting parameter names and values into bytes prior
+ * to URL encoding them.
+ *
The string encoding used when converting the URL encoded parameters into a raw
+ * byte array.
+ *
+ *
+ * @deprecated Use {@link #getParamsEncoding()} instead.
+ */
+ @Deprecated
+ protected String getPostParamsEncoding() {
+ return getParamsEncoding();
+ }
+
+ /**
+ * @deprecated Use {@link #getBodyContentType()} instead.
+ */
+ @Deprecated
+ public String getPostBodyContentType() {
+ return getBodyContentType();
+ }
+
+ /**
+ * Returns the raw POST body to be sent.
+ *
+ * @throws AuthFailureError In the event of auth failure
+ *
+ * @deprecated Use {@link #getBody()} instead.
+ */
+ @Deprecated
+ public byte[] getPostBody() throws AuthFailureError {
+ // Note: For compatibility with legacy clients of volley, this implementation must remain
+ // here instead of simply calling the getBody() function because this function must
+ // call getPostParams() and getPostParamsEncoding() since legacy clients would have
+ // overridden these two member functions for POST requests.
+ Map postParams = getPostParams();
+ if (postParams != null && postParams.size() > 0) {
+ return encodeParameters(postParams, getPostParamsEncoding());
+ }
+ return null;
+ }
+
+ /**
+ * Returns a Map of parameters to be used for a POST or PUT request. Can throw
+ * {@link AuthFailureError} as authentication may be required to provide these values.
+ *
+ *
Note that you can directly override {@link #getBody()} for custom data.
+ *
+ * @throws AuthFailureError in the event of auth failure
+ */
+ protected Map getParams() throws AuthFailureError {
+ return null;
+ }
+
+ /**
+ * Returns which encoding should be used when converting POST or PUT parameters returned by
+ * {@link #getParams()} into a raw POST or PUT body.
+ *
+ *
This controls both encodings:
+ *
+ *
The string encoding used when converting parameter names and values into bytes prior
+ * to URL encoding them.
+ *
The string encoding used when converting the URL encoded parameters into a raw
+ * byte array.
+ *
+ */
+ protected String getParamsEncoding() {
+ return DEFAULT_PARAMS_ENCODING;
+ }
+
+ /**
+ * Returns the content type of the POST or PUT body.
+ */
+ public String getBodyContentType() {
+ return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
+ }
+
+ /**
+ * Returns the raw POST or PUT body to be sent.
+ *
+ *
By default, the body consists of the request parameters in
+ * application/x-www-form-urlencoded format. When overriding this method, consider overriding
+ * {@link #getBodyContentType()} as well to match the new body format.
+ *
+ * @throws AuthFailureError in the event of auth failure
+ */
+ public byte[] getBody() throws AuthFailureError {
+ Map params = getParams();
+ if (params != null && params.size() > 0) {
+ return encodeParameters(params, getParamsEncoding());
+ }
+ return null;
+ }
+
+ /**
+ * Converts params into an application/x-www-form-urlencoded encoded string.
+ */
+ private byte[] encodeParameters(Map params, String paramsEncoding) {
+ StringBuilder encodedParams = new StringBuilder();
+ try {
+ for (Map.Entry entry : params.entrySet()) {
+ encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
+ encodedParams.append('=');
+ encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
+ encodedParams.append('&');
+ }
+ return encodedParams.toString().getBytes(paramsEncoding);
+ } catch (UnsupportedEncodingException uee) {
+ throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
+ }
+ }
+
+ /**
+ * Set whether or not responses to this request should be cached.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public final Request> setShouldCache(boolean shouldCache) {
+ mShouldCache = shouldCache;
+ return this;
+ }
+
+ /**
+ * Returns true if responses to this request should be cached.
+ */
+ public final boolean shouldCache() {
+ return mShouldCache;
+ }
+
+ /**
+ * Sets whether or not the request should be retried in the event of an HTTP 5xx (server) error.
+ *
+ * @return This Request object to allow for chaining.
+ */
+ public final Request> setShouldRetryServerErrors(boolean shouldRetryServerErrors) {
+ mShouldRetryServerErrors = shouldRetryServerErrors;
+ return this;
+ }
+
+ /**
+ * Returns true if this request should be retried in the event of an HTTP 5xx (server) error.
+ */
+ public final boolean shouldRetryServerErrors() {
+ return mShouldRetryServerErrors;
+ }
+
+ /**
+ * Priority values. Requests will be processed from higher priorities to
+ * lower priorities, in FIFO order.
+ */
+ public enum Priority {
+ LOW,
+ NORMAL,
+ HIGH,
+ IMMEDIATE
+ }
+
+ /**
+ * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
+ */
+ public Priority getPriority() {
+ return Priority.NORMAL;
+ }
+
+ /**
+ * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
+ * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
+ * attempts remaining, this will cause delivery of a {@link TimeoutError} error.
+ */
+ public final int getTimeoutMs() {
+ return mRetryPolicy.getCurrentTimeout();
+ }
+
+ /**
+ * Returns the retry policy that should be used for this request.
+ */
+ public RetryPolicy getRetryPolicy() {
+ return mRetryPolicy;
+ }
+
+ /**
+ * Mark this request as having a response delivered on it. This can be used
+ * later in the request's lifetime for suppressing identical responses.
+ */
+ public void markDelivered() {
+ mResponseDelivered = true;
+ }
+
+ /**
+ * Returns true if this request has had a response delivered for it.
+ */
+ public boolean hasHadResponseDelivered() {
+ return mResponseDelivered;
+ }
+
+ /**
+ * Subclasses must implement this to parse the raw network response
+ * and return an appropriate response type. This method will be
+ * called from a worker thread. The response will not be delivered
+ * if you return null.
+ * @param response Response from the network
+ * @return The parsed response, or null in the case of an error
+ */
+ abstract protected Response parseNetworkResponse(NetworkResponse response);
+
+ /**
+ * Subclasses can override this method to parse 'networkError' and return a more specific error.
+ *
+ *
The default implementation just returns the passed 'networkError'.
+ *
+ * @param volleyError the error retrieved from the network
+ * @return an NetworkError augmented with additional information
+ */
+ protected VolleyError parseNetworkError(VolleyError volleyError) {
+ return volleyError;
+ }
+
+ /**
+ * Subclasses must implement this to perform delivery of the parsed
+ * response to their listeners. The given response is guaranteed to
+ * be non-null; responses that fail to parse are not delivered.
+ * @param response The parsed response returned by
+ * {@link #parseNetworkResponse(NetworkResponse)}
+ */
+ abstract protected void deliverResponse(T response);
+
+ /**
+ * Delivers error message to the ErrorListener that the Request was
+ * initialized with.
+ *
+ * @param error Error details
+ */
+ public void deliverError(VolleyError error) {
+ if (mErrorListener != null) {
+ mErrorListener.onErrorResponse(error);
+ }
+ }
+
+ /**
+ * Our comparator sorts from high to low priority, and secondarily by
+ * sequence number to provide FIFO ordering.
+ */
+ @Override
+ public int compareTo(Request other) {
+ Priority left = this.getPriority();
+ Priority right = other.getPriority();
+
+ // High-priority requests are "lesser" so they are sorted to the front.
+ // Equal priorities are sorted by sequence number to provide FIFO ordering.
+ return left == right ?
+ this.mSequence - other.mSequence :
+ right.ordinal() - left.ordinal();
+ }
+
+ @Override
+ public String toString() {
+ String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
+ return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
+ + getPriority() + " " + mSequence;
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/RequestQueue.java b/volley/src/main/java/com/android/volley/RequestQueue.java
new file mode 100644
index 0000000..4324590
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/RequestQueue.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A request dispatch queue with a thread pool of dispatchers.
+ *
+ * Calling {@link #add(Request)} will enqueue the given Request for dispatch,
+ * resolving from either cache or network on a worker thread, and then delivering
+ * a parsed response on the main thread.
+ */
+public class RequestQueue {
+
+ /** Callback interface for completed requests. */
+ public static interface RequestFinishedListener {
+ /** Called when a request has finished processing. */
+ public void onRequestFinished(Request request);
+ }
+
+ /** Used for generating monotonically-increasing sequence numbers for requests. */
+ private AtomicInteger mSequenceGenerator = new AtomicInteger();
+
+ /**
+ * Staging area for requests that already have a duplicate request in flight.
+ *
+ *
+ *
containsKey(cacheKey) indicates that there is a request in flight for the given cache
+ * key.
+ *
get(cacheKey) returns waiting requests for the given cache key. The in flight request
+ * is not contained in that list. Is null if no requests are staged.
+ *
+ */
+ private final Map>> mWaitingRequests =
+ new HashMap>>();
+
+ /**
+ * The set of all requests currently being processed by this RequestQueue. A Request
+ * will be in this set if it is waiting in any queue or currently being processed by
+ * any dispatcher.
+ */
+ private final Set> mCurrentRequests = new HashSet>();
+
+ /** The cache triage queue. */
+ private final PriorityBlockingQueue> mCacheQueue =
+ new PriorityBlockingQueue>();
+
+ /** The queue of requests that are actually going out to the network. */
+ private final PriorityBlockingQueue> mNetworkQueue =
+ new PriorityBlockingQueue>();
+
+ /** Number of network request dispatcher threads to start. */
+ private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
+
+ /** Cache interface for retrieving and storing responses. */
+ private final Cache mCache;
+
+ /** Network interface for performing requests. */
+ private final Network mNetwork;
+
+ /** Response delivery mechanism. */
+ private final ResponseDelivery mDelivery;
+
+ /** The network dispatchers. */
+ private NetworkDispatcher[] mDispatchers;
+
+ /** The cache dispatcher. */
+ private CacheDispatcher mCacheDispatcher;
+
+ private List mFinishedListeners =
+ new ArrayList();
+
+ /**
+ * Creates the worker pool. Processing will not begin until {@link #start()} is called.
+ *
+ * @param cache A Cache to use for persisting responses to disk
+ * @param network A Network interface for performing HTTP requests
+ * @param threadPoolSize Number of network dispatcher threads to create
+ * @param delivery A ResponseDelivery interface for posting responses and errors
+ */
+ public RequestQueue(Cache cache, Network network, int threadPoolSize,
+ ResponseDelivery delivery) {
+ mCache = cache;
+ mNetwork = network;
+ mDispatchers = new NetworkDispatcher[threadPoolSize];
+ mDelivery = delivery;
+ }
+
+ /**
+ * Creates the worker pool. Processing will not begin until {@link #start()} is called.
+ *
+ * @param cache A Cache to use for persisting responses to disk
+ * @param network A Network interface for performing HTTP requests
+ * @param threadPoolSize Number of network dispatcher threads to create
+ */
+ public RequestQueue(Cache cache, Network network, int threadPoolSize) {
+ this(cache, network, threadPoolSize,
+ new ExecutorDelivery(new Handler(Looper.getMainLooper())));
+ }
+
+ /**
+ * Creates the worker pool. Processing will not begin until {@link #start()} is called.
+ *
+ * @param cache A Cache to use for persisting responses to disk
+ * @param network A Network interface for performing HTTP requests
+ */
+ public RequestQueue(Cache cache, Network network) {
+ this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
+ }
+
+ /**
+ * Starts the dispatchers in this queue.
+ */
+ public void start() {
+ stop(); // Make sure any currently running dispatchers are stopped.
+ // Create the cache dispatcher and start it.
+ mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
+ mCacheDispatcher.start();
+
+ // Create network dispatchers (and corresponding threads) up to the pool size.
+ for (int i = 0; i < mDispatchers.length; i++) {
+ NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
+ mCache, mDelivery);
+ mDispatchers[i] = networkDispatcher;
+ networkDispatcher.start();
+ }
+ }
+
+ /**
+ * Stops the cache and network dispatchers.
+ */
+ public void stop() {
+ if (mCacheDispatcher != null) {
+ mCacheDispatcher.quit();
+ }
+ for (int i = 0; i < mDispatchers.length; i++) {
+ if (mDispatchers[i] != null) {
+ mDispatchers[i].quit();
+ }
+ }
+ }
+
+ /**
+ * Gets a sequence number.
+ */
+ public int getSequenceNumber() {
+ return mSequenceGenerator.incrementAndGet();
+ }
+
+ /**
+ * Gets the {@link Cache} instance being used.
+ */
+ public Cache getCache() {
+ return mCache;
+ }
+
+ /**
+ * A simple predicate or filter interface for Requests, for use by
+ * {@link RequestQueue#cancelAll(RequestFilter)}.
+ */
+ public interface RequestFilter {
+ public boolean apply(Request> request);
+ }
+
+ /**
+ * Cancels all requests in this queue for which the given filter applies.
+ * @param filter The filtering function to use
+ */
+ public void cancelAll(RequestFilter filter) {
+ synchronized (mCurrentRequests) {
+ for (Request> request : mCurrentRequests) {
+ if (filter.apply(request)) {
+ request.cancel();
+ }
+ }
+ }
+ }
+
+ /**
+ * Cancels all requests in this queue with the given tag. Tag must be non-null
+ * and equality is by identity.
+ */
+ public void cancelAll(final Object tag) {
+ if (tag == null) {
+ throw new IllegalArgumentException("Cannot cancelAll with a null tag");
+ }
+ cancelAll(new RequestFilter() {
+ @Override
+ public boolean apply(Request> request) {
+ return request.getTag() == tag;
+ }
+ });
+ }
+
+ /**
+ * Adds a Request to the dispatch queue.
+ * @param request The request to service
+ * @return The passed-in request
+ */
+ public Request add(Request request) {
+ // Tag the request as belonging to this queue and add it to the set of current requests.
+ request.setRequestQueue(this);
+ synchronized (mCurrentRequests) {
+ mCurrentRequests.add(request);
+ }
+
+ // Process requests in the order they are added.
+ request.setSequence(getSequenceNumber());
+ request.addMarker("add-to-queue");
+
+ // If the request is uncacheable, skip the cache queue and go straight to the network.
+ if (!request.shouldCache()) {
+ mNetworkQueue.add(request);
+ return request;
+ }
+
+ // Insert request into stage if there's already a request with the same cache key in flight.
+ synchronized (mWaitingRequests) {
+ String cacheKey = request.getCacheKey();
+ if (mWaitingRequests.containsKey(cacheKey)) {
+ // There is already a request in flight. Queue up.
+ Queue> stagedRequests = mWaitingRequests.get(cacheKey);
+ if (stagedRequests == null) {
+ stagedRequests = new LinkedList>();
+ }
+ stagedRequests.add(request);
+ mWaitingRequests.put(cacheKey, stagedRequests);
+ if (VolleyLog.DEBUG) {
+ VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
+ }
+ } else {
+ // Insert 'null' queue for this cacheKey, indicating there is now a request in
+ // flight.
+ mWaitingRequests.put(cacheKey, null);
+ mCacheQueue.add(request);
+ }
+ return request;
+ }
+ }
+
+ /**
+ * Called from {@link Request#finish(String)}, indicating that processing of the given request
+ * has finished.
+ *
+ *
Releases waiting requests for request.getCacheKey() if
+ * request.shouldCache().
+ */
+ void finish(Request request) {
+ // Remove from the set of requests currently being processed.
+ synchronized (mCurrentRequests) {
+ mCurrentRequests.remove(request);
+ }
+ synchronized (mFinishedListeners) {
+ for (RequestFinishedListener listener : mFinishedListeners) {
+ listener.onRequestFinished(request);
+ }
+ }
+
+ if (request.shouldCache()) {
+ synchronized (mWaitingRequests) {
+ String cacheKey = request.getCacheKey();
+ Queue> waitingRequests = mWaitingRequests.remove(cacheKey);
+ if (waitingRequests != null) {
+ if (VolleyLog.DEBUG) {
+ VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
+ waitingRequests.size(), cacheKey);
+ }
+ // Process all queued up requests. They won't be considered as in flight, but
+ // that's not a problem as the cache has been primed by 'request'.
+ mCacheQueue.addAll(waitingRequests);
+ }
+ }
+ }
+ }
+
+ public void addRequestFinishedListener(RequestFinishedListener listener) {
+ synchronized (mFinishedListeners) {
+ mFinishedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove a RequestFinishedListener. Has no effect if listener was not previously added.
+ */
+ public void removeRequestFinishedListener(RequestFinishedListener listener) {
+ synchronized (mFinishedListeners) {
+ mFinishedListeners.remove(listener);
+ }
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/Response.java b/volley/src/main/java/com/android/volley/Response.java
new file mode 100644
index 0000000..1165595
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/Response.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Encapsulates a parsed response for delivery.
+ *
+ * @param Parsed type of this response
+ */
+public class Response {
+
+ /** Callback interface for delivering parsed responses. */
+ public interface Listener {
+ /** Called when a response is received. */
+ public void onResponse(T response);
+ }
+
+ /** Callback interface for delivering error responses. */
+ public interface ErrorListener {
+ /**
+ * Callback method that an error has been occurred with the
+ * provided error code and optional user-readable message.
+ */
+ public void onErrorResponse(VolleyError error);
+ }
+
+ /** Returns a successful response containing the parsed result. */
+ public static Response success(T result, Cache.Entry cacheEntry) {
+ return new Response(result, cacheEntry);
+ }
+
+ /**
+ * Returns a failed response containing the given error code and an optional
+ * localized message displayed to the user.
+ */
+ public static Response error(VolleyError error) {
+ return new Response(error);
+ }
+
+ /** Parsed response, or null in the case of error. */
+ public final T result;
+
+ /** Cache metadata for this response, or null in the case of error. */
+ public final Cache.Entry cacheEntry;
+
+ /** Detailed error information if errorCode != OK. */
+ public final VolleyError error;
+
+ /** True if this response was a soft-expired one and a second one MAY be coming. */
+ public boolean intermediate = false;
+
+ /**
+ * Returns whether this response is considered successful.
+ */
+ public boolean isSuccess() {
+ return error == null;
+ }
+
+
+ private Response(T result, Cache.Entry cacheEntry) {
+ this.result = result;
+ this.cacheEntry = cacheEntry;
+ this.error = null;
+ }
+
+ private Response(VolleyError error) {
+ this.result = null;
+ this.cacheEntry = null;
+ this.error = error;
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/ResponseDelivery.java b/volley/src/main/java/com/android/volley/ResponseDelivery.java
new file mode 100644
index 0000000..87706af
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/ResponseDelivery.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+public interface ResponseDelivery {
+ /**
+ * Parses a response from the network or cache and delivers it.
+ */
+ public void postResponse(Request> request, Response> response);
+
+ /**
+ * Parses a response from the network or cache and delivers it. The provided
+ * Runnable will be executed after delivery.
+ */
+ public void postResponse(Request> request, Response> response, Runnable runnable);
+
+ /**
+ * Posts an error for the given request.
+ */
+ public void postError(Request> request, VolleyError error);
+}
diff --git a/volley/src/main/java/com/android/volley/RetryPolicy.java b/volley/src/main/java/com/android/volley/RetryPolicy.java
new file mode 100644
index 0000000..0dd198b
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/RetryPolicy.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Retry policy for a request.
+ */
+public interface RetryPolicy {
+
+ /**
+ * Returns the current timeout (used for logging).
+ */
+ public int getCurrentTimeout();
+
+ /**
+ * Returns the current retry count (used for logging).
+ */
+ public int getCurrentRetryCount();
+
+ /**
+ * Prepares for the next retry by applying a backoff to the timeout.
+ * @param error The error code of the last attempt.
+ * @throws VolleyError In the event that the retry could not be performed (for example if we
+ * ran out of attempts), the passed in error is thrown.
+ */
+ public void retry(VolleyError error) throws VolleyError;
+}
diff --git a/volley/src/main/java/com/android/volley/ServerError.java b/volley/src/main/java/com/android/volley/ServerError.java
new file mode 100644
index 0000000..7b33c33
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/ServerError.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Indicates that the server responded with an error response.
+ */
+@SuppressWarnings("serial")
+public class ServerError extends VolleyError {
+ public ServerError(NetworkResponse networkResponse) {
+ super(networkResponse);
+ }
+
+ public ServerError() {
+ super();
+ }
+}
+
diff --git a/volley/src/main/java/com/android/volley/TimeoutError.java b/volley/src/main/java/com/android/volley/TimeoutError.java
new file mode 100644
index 0000000..0b5d6ac
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/TimeoutError.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Indicates that the connection or the socket timed out.
+ */
+@SuppressWarnings("serial")
+public class TimeoutError extends VolleyError { }
diff --git a/volley/src/main/java/com/android/volley/VolleyError.java b/volley/src/main/java/com/android/volley/VolleyError.java
new file mode 100644
index 0000000..1471d40
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/VolleyError.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+/**
+ * Exception style class encapsulating Volley errors
+ */
+@SuppressWarnings("serial")
+public class VolleyError extends Exception {
+ public final NetworkResponse networkResponse;
+ private long networkTimeMs;
+
+ public VolleyError() {
+ networkResponse = null;
+ }
+
+ public VolleyError(NetworkResponse response) {
+ networkResponse = response;
+ }
+
+ public VolleyError(String exceptionMessage) {
+ super(exceptionMessage);
+ networkResponse = null;
+ }
+
+ public VolleyError(String exceptionMessage, Throwable reason) {
+ super(exceptionMessage, reason);
+ networkResponse = null;
+ }
+
+ public VolleyError(Throwable cause) {
+ super(cause);
+ networkResponse = null;
+ }
+
+ /* package */ void setNetworkTimeMs(long networkTimeMs) {
+ this.networkTimeMs = networkTimeMs;
+ }
+
+ public long getNetworkTimeMs() {
+ return networkTimeMs;
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/VolleyLog.java b/volley/src/main/java/com/android/volley/VolleyLog.java
new file mode 100644
index 0000000..ffe9eb8
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/VolleyLog.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Logging helper class.
+ *
+ * to see Volley logs call:
+ * {@code /platform-tools/adb shell setprop log.tag.Volley VERBOSE}
+ */
+public class VolleyLog {
+ public static String TAG = "Volley";
+
+ public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
+
+ /**
+ * Customize the log tag for your application, so that other apps
+ * using Volley don't mix their logs with yours.
+ *
+ * Enable the log property for your tag before starting your app:
+ *
+ * {@code adb shell setprop log.tag.<tag>}
+ */
+ public static void setTag(String tag) {
+ d("Changing log tag to %s", tag);
+ TAG = tag;
+
+ // Reinitialize the DEBUG "constant"
+ DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ public static void v(String format, Object... args) {
+ if (DEBUG) {
+ Log.v(TAG, buildMessage(format, args));
+ }
+ }
+
+ public static void d(String format, Object... args) {
+ Log.d(TAG, buildMessage(format, args));
+ }
+
+ public static void e(String format, Object... args) {
+ Log.e(TAG, buildMessage(format, args));
+ }
+
+ public static void e(Throwable tr, String format, Object... args) {
+ Log.e(TAG, buildMessage(format, args), tr);
+ }
+
+ public static void wtf(String format, Object... args) {
+ Log.wtf(TAG, buildMessage(format, args));
+ }
+
+ public static void wtf(Throwable tr, String format, Object... args) {
+ Log.wtf(TAG, buildMessage(format, args), tr);
+ }
+
+ /**
+ * Formats the caller's provided message and prepends useful info like
+ * calling thread ID and method name.
+ */
+ private static String buildMessage(String format, Object... args) {
+ String msg = (args == null) ? format : String.format(Locale.US, format, args);
+ StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
+
+ String caller = "";
+ // Walk up the stack looking for the first caller outside of VolleyLog.
+ // It will be at least two frames up, so start there.
+ for (int i = 2; i < trace.length; i++) {
+ Class> clazz = trace[i].getClass();
+ if (!clazz.equals(VolleyLog.class)) {
+ String callingClass = trace[i].getClassName();
+ callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
+ callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
+
+ caller = callingClass + "." + trace[i].getMethodName();
+ break;
+ }
+ }
+ return String.format(Locale.US, "[%d] %s: %s",
+ Thread.currentThread().getId(), caller, msg);
+ }
+
+ /**
+ * A simple event log with records containing a name, thread ID, and timestamp.
+ */
+ static class MarkerLog {
+ public static final boolean ENABLED = VolleyLog.DEBUG;
+
+ /** Minimum duration from first marker to last in an marker log to warrant logging. */
+ private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
+
+ private static class Marker {
+ public final String name;
+ public final long thread;
+ public final long time;
+
+ public Marker(String name, long thread, long time) {
+ this.name = name;
+ this.thread = thread;
+ this.time = time;
+ }
+ }
+
+ private final List mMarkers = new ArrayList();
+ private boolean mFinished = false;
+
+ /** Adds a marker to this log with the specified name. */
+ public synchronized void add(String name, long threadId) {
+ if (mFinished) {
+ throw new IllegalStateException("Marker added to finished log");
+ }
+
+ mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
+ }
+
+ /**
+ * Closes the log, dumping it to logcat if the time difference between
+ * the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.
+ * @param header Header string to print above the marker log.
+ */
+ public synchronized void finish(String header) {
+ mFinished = true;
+
+ long duration = getTotalDuration();
+ if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
+ return;
+ }
+
+ long prevTime = mMarkers.get(0).time;
+ d("(%-4d ms) %s", duration, header);
+ for (Marker marker : mMarkers) {
+ long thisTime = marker.time;
+ d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
+ prevTime = thisTime;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ // Catch requests that have been collected (and hence end-of-lifed)
+ // but had no debugging output printed for them.
+ if (!mFinished) {
+ finish("Request on the loose");
+ e("Marker log finalized without finish() - uncaught exit point for request");
+ }
+ }
+
+ /** Returns the time difference between the first and last events in this log. */
+ private long getTotalDuration() {
+ if (mMarkers.size() == 0) {
+ return 0;
+ }
+
+ long first = mMarkers.get(0).time;
+ long last = mMarkers.get(mMarkers.size() - 1).time;
+ return last - first;
+ }
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java b/volley/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java
new file mode 100644
index 0000000..18f8597
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/toolbox/AndroidAuthenticator.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import com.android.volley.AuthFailureError;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * An Authenticator that uses {@link AccountManager} to get auth
+ * tokens of a specified type for a specified account.
+ */
+public class AndroidAuthenticator implements Authenticator {
+ private final AccountManager mAccountManager;
+ private final Account mAccount;
+ private final String mAuthTokenType;
+ private final boolean mNotifyAuthFailure;
+
+ /**
+ * Creates a new authenticator.
+ * @param context Context for accessing AccountManager
+ * @param account Account to authenticate as
+ * @param authTokenType Auth token type passed to AccountManager
+ */
+ public AndroidAuthenticator(Context context, Account account, String authTokenType) {
+ this(context, account, authTokenType, false);
+ }
+
+ /**
+ * Creates a new authenticator.
+ * @param context Context for accessing AccountManager
+ * @param account Account to authenticate as
+ * @param authTokenType Auth token type passed to AccountManager
+ * @param notifyAuthFailure Whether to raise a notification upon auth failure
+ */
+ public AndroidAuthenticator(Context context, Account account, String authTokenType,
+ boolean notifyAuthFailure) {
+ this(AccountManager.get(context), account, authTokenType, notifyAuthFailure);
+ }
+
+ // Visible for testing. Allows injection of a mock AccountManager.
+ AndroidAuthenticator(AccountManager accountManager, Account account,
+ String authTokenType, boolean notifyAuthFailure) {
+ mAccountManager = accountManager;
+ mAccount = account;
+ mAuthTokenType = authTokenType;
+ mNotifyAuthFailure = notifyAuthFailure;
+ }
+
+ /**
+ * Returns the Account being used by this authenticator.
+ */
+ public Account getAccount() {
+ return mAccount;
+ }
+
+ /**
+ * Returns the Auth Token Type used by this authenticator.
+ */
+ public String getAuthTokenType() {
+ return mAuthTokenType;
+ }
+
+ // TODO: Figure out what to do about notifyAuthFailure
+ @SuppressWarnings("deprecation")
+ @Override
+ public String getAuthToken() throws AuthFailureError {
+ AccountManagerFuture future = mAccountManager.getAuthToken(mAccount,
+ mAuthTokenType, mNotifyAuthFailure, null, null);
+ Bundle result;
+ try {
+ result = future.getResult();
+ } catch (Exception e) {
+ throw new AuthFailureError("Error while retrieving auth token", e);
+ }
+ String authToken = null;
+ if (future.isDone() && !future.isCancelled()) {
+ if (result.containsKey(AccountManager.KEY_INTENT)) {
+ Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+ throw new AuthFailureError(intent);
+ }
+ authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ }
+ if (authToken == null) {
+ throw new AuthFailureError("Got null auth token for type: " + mAuthTokenType);
+ }
+
+ return authToken;
+ }
+
+ @Override
+ public void invalidateAuthToken(String authToken) {
+ mAccountManager.invalidateAuthToken(mAccount.type, authToken);
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/toolbox/Authenticator.java b/volley/src/main/java/com/android/volley/toolbox/Authenticator.java
new file mode 100644
index 0000000..d9f5e3c
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/toolbox/Authenticator.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import com.android.volley.AuthFailureError;
+
+/**
+ * An interface for interacting with auth tokens.
+ */
+public interface Authenticator {
+ /**
+ * Synchronously retrieves an auth token.
+ *
+ * @throws AuthFailureError If authentication did not succeed
+ */
+ public String getAuthToken() throws AuthFailureError;
+
+ /**
+ * Invalidates the provided auth token.
+ */
+ public void invalidateAuthToken(String authToken);
+}
diff --git a/volley/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/volley/src/main/java/com/android/volley/toolbox/BasicNetwork.java
new file mode 100644
index 0000000..37c35ec
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/toolbox/BasicNetwork.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import android.os.SystemClock;
+
+import com.android.volley.AuthFailureError;
+import com.android.volley.Cache;
+import com.android.volley.Cache.Entry;
+import com.android.volley.ClientError;
+import com.android.volley.Network;
+import com.android.volley.NetworkError;
+import com.android.volley.NetworkResponse;
+import com.android.volley.NoConnectionError;
+import com.android.volley.Request;
+import com.android.volley.RetryPolicy;
+import com.android.volley.ServerError;
+import com.android.volley.TimeoutError;
+import com.android.volley.VolleyError;
+import com.android.volley.VolleyLog;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.impl.cookie.DateUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A network performing Volley requests over an {@link HttpStack}.
+ */
+public class BasicNetwork implements Network {
+ protected static final boolean DEBUG = VolleyLog.DEBUG;
+
+ private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
+
+ private static int DEFAULT_POOL_SIZE = 4096;
+
+ protected final HttpStack mHttpStack;
+
+ protected final ByteArrayPool mPool;
+
+ /**
+ * @param httpStack HTTP stack to be used
+ */
+ public BasicNetwork(HttpStack httpStack) {
+ // If a pool isn't passed in, then build a small default pool that will give us a lot of
+ // benefit and not use too much memory.
+ this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
+ }
+
+ /**
+ * @param httpStack HTTP stack to be used
+ * @param pool a buffer pool that improves GC performance in copy operations
+ */
+ public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
+ mHttpStack = httpStack;
+ mPool = pool;
+ }
+
+ @Override
+ public NetworkResponse performRequest(Request> request) throws VolleyError {
+ long requestStart = SystemClock.elapsedRealtime();
+ while (true) {
+ HttpResponse httpResponse = null;
+ byte[] responseContents = null;
+ Map responseHeaders = Collections.emptyMap();
+ try {
+ // Gather headers.
+ Map headers = new HashMap();
+ addCacheHeaders(headers, request.getCacheEntry());
+ httpResponse = mHttpStack.performRequest(request, headers);
+ StatusLine statusLine = httpResponse.getStatusLine();
+ int statusCode = statusLine.getStatusCode();
+
+ responseHeaders = convertHeaders(httpResponse.getAllHeaders());
+ // Handle cache validation.
+ if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
+
+ Entry entry = request.getCacheEntry();
+ if (entry == null) {
+ return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
+ responseHeaders, true,
+ SystemClock.elapsedRealtime() - requestStart);
+ }
+
+ // A HTTP 304 response does not have all header fields. We
+ // have to use the header fields from the cache entry plus
+ // the new ones from the response.
+ // 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,
+ SystemClock.elapsedRealtime() - requestStart);
+ }
+
+ // Some responses such as 204s do not have content. We must check.
+ if (httpResponse.getEntity() != null) {
+ responseContents = entityToBytes(httpResponse.getEntity());
+ } else {
+ // Add 0 byte response as a way of honestly representing a
+ // no-content request.
+ responseContents = new byte[0];
+ }
+
+ // if the request is slow, log it.
+ long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
+ logSlowRequests(requestLifetime, request, responseContents, statusLine);
+
+ if (statusCode < 200 || statusCode > 299) {
+ throw new IOException();
+ }
+ return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
+ SystemClock.elapsedRealtime() - requestStart);
+ } catch (SocketTimeoutException e) {
+ attemptRetryOnException("socket", request, new TimeoutError());
+ } catch (ConnectTimeoutException e) {
+ attemptRetryOnException("connection", request, new TimeoutError());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Bad URL " + request.getUrl(), e);
+ } catch (IOException e) {
+ int statusCode;
+ if (httpResponse != null) {
+ statusCode = httpResponse.getStatusLine().getStatusCode();
+ } else {
+ throw new NoConnectionError(e);
+ }
+ VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
+ NetworkResponse networkResponse;
+ if (responseContents != null) {
+ networkResponse = new NetworkResponse(statusCode, responseContents,
+ responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
+ if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
+ statusCode == HttpStatus.SC_FORBIDDEN) {
+ attemptRetryOnException("auth",
+ request, new AuthFailureError(networkResponse));
+ } else if (statusCode >= 400 && statusCode <= 499) {
+ // Don't retry other client errors.
+ throw new ClientError(networkResponse);
+ } else if (statusCode >= 500 && statusCode <= 599) {
+ if (request.shouldRetryServerErrors()) {
+ attemptRetryOnException("server",
+ request, new ServerError(networkResponse));
+ } else {
+ throw new ServerError(networkResponse);
+ }
+ } else {
+ // 3xx? No reason to retry.
+ throw new ServerError(networkResponse);
+ }
+ } else {
+ attemptRetryOnException("network", request, new NetworkError());
+ }
+ }
+ }
+ }
+
+ /**
+ * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
+ */
+ private void logSlowRequests(long requestLifetime, Request> request,
+ byte[] responseContents, StatusLine statusLine) {
+ if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
+ VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
+ "[rc=%d], [retryCount=%s]", request, requestLifetime,
+ responseContents != null ? responseContents.length : "null",
+ statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
+ }
+ }
+
+ /**
+ * Attempts to prepare the request for a retry. If there are no more attempts remaining in the
+ * request's retry policy, a timeout exception is thrown.
+ * @param request The request to use.
+ */
+ private static void attemptRetryOnException(String logPrefix, Request> request,
+ VolleyError exception) throws VolleyError {
+ RetryPolicy retryPolicy = request.getRetryPolicy();
+ int oldTimeout = request.getTimeoutMs();
+
+ try {
+ retryPolicy.retry(exception);
+ } catch (VolleyError e) {
+ request.addMarker(
+ String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
+ throw e;
+ }
+ request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
+ }
+
+ private void addCacheHeaders(Map headers, Cache.Entry entry) {
+ // If there's no cache entry, we're done.
+ if (entry == null) {
+ return;
+ }
+
+ if (entry.etag != null) {
+ headers.put("If-None-Match", entry.etag);
+ }
+
+ if (entry.lastModified > 0) {
+ Date refTime = new Date(entry.lastModified);
+ headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
+ }
+ }
+
+ protected void logError(String what, String url, long start) {
+ long now = SystemClock.elapsedRealtime();
+ VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
+ }
+
+ /** Reads the contents of HttpEntity into a byte[]. */
+ private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
+ PoolingByteArrayOutputStream bytes =
+ new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
+ byte[] buffer = null;
+ try {
+ InputStream in = entity.getContent();
+ if (in == null) {
+ throw new ServerError();
+ }
+ buffer = mPool.getBuf(1024);
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+ return bytes.toByteArray();
+ } finally {
+ try {
+ // Close the InputStream and release the resources by "consuming the content".
+ entity.consumeContent();
+ } catch (IOException e) {
+ // This can happen if there was an exception above that left the entity in
+ // an invalid state.
+ VolleyLog.v("Error occured when calling consumingContent");
+ }
+ mPool.returnBuf(buffer);
+ bytes.close();
+ }
+ }
+
+ /**
+ * Converts Headers[] to Map.
+ */
+ protected static Map convertHeaders(Header[] headers) {
+ Map result = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+ for (int i = 0; i < headers.length; i++) {
+ result.put(headers[i].getName(), headers[i].getValue());
+ }
+ return result;
+ }
+}
diff --git a/volley/src/main/java/com/android/volley/toolbox/ByteArrayPool.java b/volley/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
new file mode 100644
index 0000000..af95076
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * ByteArrayPool is a source and repository of byte[] objects. Its purpose is to
+ * supply those buffers to consumers who need to use them for a short period of time and then
+ * dispose of them. Simply creating and disposing such buffers in the conventional manner can
+ * considerable heap churn and garbage collection delays on Android, which lacks good management of
+ * short-lived heap objects. It may be advantageous to trade off some memory in the form of a
+ * permanently allocated pool of buffers in order to gain heap performance improvements; that is
+ * what this class does.
+ *
+ * A good candidate user for this class is something like an I/O system that uses large temporary
+ * byte[] buffers to copy data around. In these use cases, often the consumer wants
+ * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
+ * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
+ * account and also to maximize the odds of being able to reuse a recycled buffer, this class is
+ * free to return buffers larger than the requested size. The caller needs to be able to gracefully
+ * deal with getting buffers any size over the minimum.
+ *
+ * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
+ * class will allocate a new buffer and return it.
+ *
+ * This class has no special ownership of buffers it creates; the caller is free to take a buffer
+ * it receives from this pool, use it permanently, and never return it to the pool; additionally,
+ * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
+ * are no other lingering references to it.
+ *
+ * This class ensures that the total size of the buffers in its recycling pool never exceeds a
+ * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
+ * least-recently-used buffers are disposed.
+ */
+public class ByteArrayPool {
+ /** The buffer pool, arranged both by last use and by buffer size */
+ private List mBuffersByLastUse = new LinkedList();
+ private List mBuffersBySize = new ArrayList(64);
+
+ /** The total size of the buffers in the pool */
+ private int mCurrentSize = 0;
+
+ /**
+ * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
+ * under this limit.
+ */
+ private final int mSizeLimit;
+
+ /** Compares buffers by size */
+ protected static final Comparator BUF_COMPARATOR = new Comparator() {
+ @Override
+ public int compare(byte[] lhs, byte[] rhs) {
+ return lhs.length - rhs.length;
+ }
+ };
+
+ /**
+ * @param sizeLimit the maximum size of the pool, in bytes
+ */
+ public ByteArrayPool(int sizeLimit) {
+ mSizeLimit = sizeLimit;
+ }
+
+ /**
+ * Returns a buffer from the pool if one is available in the requested size, or allocates a new
+ * one if a pooled one is not available.
+ *
+ * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
+ * larger.
+ * @return a byte[] buffer is always returned.
+ */
+ public synchronized byte[] getBuf(int len) {
+ for (int i = 0; i < mBuffersBySize.size(); i++) {
+ byte[] buf = mBuffersBySize.get(i);
+ if (buf.length >= len) {
+ mCurrentSize -= buf.length;
+ mBuffersBySize.remove(i);
+ mBuffersByLastUse.remove(buf);
+ return buf;
+ }
+ }
+ return new byte[len];
+ }
+
+ /**
+ * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
+ * size.
+ *
+ * @param buf the buffer to return to the pool.
+ */
+ public synchronized void returnBuf(byte[] buf) {
+ if (buf == null || buf.length > mSizeLimit) {
+ return;
+ }
+ mBuffersByLastUse.add(buf);
+ int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
+ if (pos < 0) {
+ pos = -pos - 1;
+ }
+ mBuffersBySize.add(pos, buf);
+ mCurrentSize += buf.length;
+ trim();
+ }
+
+ /**
+ * Removes buffers from the pool until it is under its size limit.
+ */
+ private synchronized void trim() {
+ while (mCurrentSize > mSizeLimit) {
+ byte[] buf = mBuffersByLastUse.remove(0);
+ mBuffersBySize.remove(buf);
+ mCurrentSize -= buf.length;
+ }
+ }
+
+}
diff --git a/volley/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java b/volley/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java
new file mode 100644
index 0000000..a3478bf
--- /dev/null
+++ b/volley/src/main/java/com/android/volley/toolbox/ClearCacheRequest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import com.android.volley.Cache;
+import com.android.volley.NetworkResponse;
+import com.android.volley.Request;
+import com.android.volley.Response;
+
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * A synthetic request used for clearing the cache.
+ */
+public class ClearCacheRequest extends Request