diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 000000000000..d64569567334
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000000..348a3a6cb105
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,166 @@
+
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+ com.squareup.okhttp
+ okhttp
+ 20120723
+ jar
+
+ okhttp
+ An HTTP+SPDY client for Android and Java applications
+ https://github.com/square/okhttp
+
+
+ UTF-8
+
+
+ 1.6
+ 8.1.2.v20120308
+ 20120401
+
+
+ 3.8.2
+
+
+
+ https://github.com/square/okhttp/
+ scm:git:https://github.com/square/okhttp.git
+ scm:git:git@github.com:square/okhttp.git
+
+
+
+ GitHub Issues
+ https://github.com/square/okhttp/issues
+
+
+
+
+ Apache 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+
+
+ org.mortbay.jetty.npn
+ npn-boot
+ ${npn.version}
+
+
+ com.google.mockwebserver
+ mockwebserver
+ ${mockwebserver.version}
+ test
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5
+
+
+ ${java.version}
+
+
+
+ org.sonatype.plugins
+ jarjar-maven-plugin
+
+
+ package
+
+ jarjar
+
+
+
+ asm:asm
+ org.sonatype.sisu.inject:cglib
+
+
+
+ libcore.**
+ com.squareup.okhttp.libcore.@1
+
+
+ com.squareup.okhttp.**
+
+
+
+
+
+
+
+ maven-surefire-plugin
+
+ -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+ verify
+ jar-no-fork
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+ jar
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/squareup/okhttp/OkHttpConnection.java b/src/main/java/com/squareup/okhttp/OkHttpConnection.java
new file mode 100644
index 000000000000..d98d330ec326
--- /dev/null
+++ b/src/main/java/com/squareup/okhttp/OkHttpConnection.java
@@ -0,0 +1,808 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.squareup.okhttp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.SocketPermission;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import libcore.net.http.HttpEngine;
+
+/**
+ * An {@link java.net.URLConnection} for HTTP (RFC 2616) used to send and
+ * receive data over the web. Data may be of any type and length. This class may
+ * be used to send and receive streaming data whose length is not known in
+ * advance.
+ *
+ *
Uses of this class follow a pattern:
+ *
+ *
Obtain a new {@code HttpURLConnection} by calling {@link
+ * java.net.URL#openConnection() URL.openConnection()} and casting the result to
+ * {@code HttpURLConnection}.
+ *
Prepare the request. The primary property of a request is its URI.
+ * Request headers may also include metadata such as credentials, preferred
+ * content types, and session cookies.
+ *
Optionally upload a request body. Instances must be configured with
+ * {@link #setDoOutput(boolean) setDoOutput(true)} if they include a
+ * request body. Transmit data by writing to the stream returned by {@link
+ * #getOutputStream()}.
+ *
Read the response. Response headers typically include metadata such as
+ * the response body's content type and length, modified dates and session
+ * cookies. The response body may be read from the stream returned by {@link
+ * #getInputStream()}. If the response has no body, that method returns an
+ * empty stream.
+ *
Disconnect. Once the response body has been read, the {@code
+ * HttpURLConnection} should be closed by calling {@link #disconnect()}.
+ * Disconnecting releases the resources held by a connection so they may
+ * be closed or reused.
+ *
+ *
+ *
For example, to retrieve the webpage at {@code http://www.android.com/}:
+ *
+ * Calling {@link java.net.URL#openConnection()} on a URL with the "https"
+ * scheme will return an {@code HttpsURLConnection}, which allows for
+ * overriding the default {@link javax.net.ssl.HostnameVerifier
+ * HostnameVerifier} and {@link javax.net.ssl.SSLSocketFactory
+ * SSLSocketFactory}. An application-supplied {@code SSLSocketFactory}
+ * created from an {@link javax.net.ssl.SSLContext SSLContext} can
+ * provide a custom {@link javax.net.ssl.X509TrustManager
+ * X509TrustManager} for verifying certificate chains and a custom
+ * {@link javax.net.ssl.X509KeyManager X509KeyManager} for supplying
+ * client certificates. See {@link OkHttpsConnection HttpsURLConnection} for
+ * more details.
+ *
+ *
Response Handling
+ * {@code HttpURLConnection} will follow up to five HTTP redirects. It will
+ * follow redirects from one origin server to another. This implementation
+ * doesn't follow redirects from HTTPS to HTTP or vice versa.
+ *
+ *
If the HTTP response indicates that an error occurred, {@link
+ * #getInputStream()} will throw an {@link java.io.IOException}. Use {@link
+ * #getErrorStream()} to read the error response. The headers can be read in
+ * the normal way using {@link #getHeaderFields()},
+ *
+ *
Posting Content
+ * To upload data to a web server, configure the connection for output using
+ * {@link #setDoOutput(boolean) setDoOutput(true)}.
+ *
+ *
For best performance, you should call either {@link
+ * #setFixedLengthStreamingMode(int)} when the body length is known in advance,
+ * or {@link #setChunkedStreamingMode(int)} when it is not. Otherwise {@code
+ * HttpURLConnection} will be forced to buffer the complete request body in
+ * memory before it is transmitted, wasting (and possibly exhausting) heap and
+ * increasing latency.
+ *
+ *
+ * The input and output streams returned by this class are not
+ * buffered. Most callers should wrap the returned streams with {@link
+ * java.io.BufferedInputStream BufferedInputStream} or {@link
+ * java.io.BufferedOutputStream BufferedOutputStream}. Callers that do only bulk
+ * reads or writes may omit buffering.
+ *
+ *
When transferring large amounts of data to or from a server, use streams
+ * to limit how much data is in memory at once. Unless you need the entire
+ * body to be in memory at once, process it as a stream (rather than storing
+ * the complete body as a single byte array or string).
+ *
+ *
To reduce latency, this class may reuse the same underlying {@code Socket}
+ * for multiple request/response pairs. As a result, HTTP connections may be
+ * held open longer than necessary. Calls to {@link #disconnect()} may return
+ * the socket to a pool of connected sockets. This behavior can be disabled by
+ * setting the {@code http.keepAlive} system property to {@code false} before
+ * issuing any HTTP requests. The {@code http.maxConnections} property may be
+ * used to control how many idle connections to each server will be held.
+ *
+ *
By default, this implementation of {@code HttpURLConnection} requests that
+ * servers use gzip compression. Since {@link #getContentLength()} returns the
+ * number of bytes transmitted, you cannot use that method to predict how many
+ * bytes can be read from {@link #getInputStream()}. Instead, read that stream
+ * until it is exhausted: when {@link java.io.InputStream#read} returns -1. Gzip
+ * compression can be disabled by setting the acceptable encodings in the
+ * request header:
+ * Some Wi-Fi networks block Internet access until the user clicks through a
+ * sign-on page. Such sign-on pages are typically presented by using HTTP
+ * redirects. You can use {@link #getURL()} to test if your connection has been
+ * unexpectedly redirected. This check is not valid until after
+ * the response headers have been received, which you can trigger by calling
+ * {@link #getHeaderFields()} or {@link #getInputStream()}. For example, to
+ * check that a response was not redirected to an unexpected host:
+ *
{@code
+ * HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ * try {
+ * InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+ * if (!url.getHost().equals(urlConnection.getURL().getHost())) {
+ * // we were redirected! Kick the user out to the browser to sign on?
+ * }
+ * ...
+ * } finally {
+ * urlConnection.disconnect();
+ * }
+ * }
+ *
+ *
HTTP Authentication
+ * {@code HttpURLConnection} supports HTTP basic authentication. Use
+ * {@link java.net.Authenticator} to set the VM-wide authentication handler:
+ *
+ * Unless paired with HTTPS, this is not a secure mechanism for
+ * user authentication. In particular, the username, password, request and
+ * response are all transmitted over the network without encryption.
+ *
+ *
Sessions with Cookies
+ * To establish and maintain a potentially long-lived session between client
+ * and server, {@code HttpURLConnection} includes an extensible cookie manager.
+ * Enable VM-wide cookie management using {@link java.net.CookieHandler} and {@link
+ * java.net.CookieManager}:
+ * By default, {@code CookieManager} accepts cookies from the origin
+ * server only. Two other policies are included: {@link
+ * java.net.CookiePolicy#ACCEPT_ALL} and {@link java.net.CookiePolicy#ACCEPT_NONE}. Implement
+ * {@link java.net.CookiePolicy} to define a custom policy.
+ *
+ *
The default {@code CookieManager} keeps all accepted cookies in memory. It
+ * will forget these cookies when the VM exits. Implement {@link java.net.CookieStore} to
+ * define a custom cookie store.
+ *
+ *
In addition to the cookies set by HTTP responses, you may set cookies
+ * programmatically. To be included in HTTP request headers, cookies must have
+ * the domain and path properties set.
+ *
+ *
By default, new instances of {@code HttpCookie} work only with servers
+ * that support RFC 2965
+ * cookies. Many web servers support only the older specification, RFC 2109. For compatibility
+ * with the most web servers, set the cookie version to 0.
+ *
+ *
For example, to receive {@code www.twitter.com} in French:
{@code HttpURLConnection} uses the {@code GET} method by default. It will
+ * use {@code POST} if {@link #setDoOutput setDoOutput(true)} has been called.
+ * Other HTTP methods ({@code OPTIONS}, {@code HEAD}, {@code PUT}, {@code
+ * DELETE} and {@code TRACE}) can be used with {@link #setRequestMethod}.
+ *
+ *
Proxies
+ * By default, this class will connect directly to the origin
+ * server. It can also connect via an {@link java.net.Proxy.Type#HTTP HTTP} or {@link
+ * java.net.Proxy.Type#SOCKS SOCKS} proxy. To use a proxy, use {@link
+ * java.net.URL#openConnection(java.net.Proxy) URL.openConnection(Proxy)} when creating the
+ * connection.
+ *
+ *
IPv6 Support
+ *
This class includes transparent support for IPv6. For hosts with both IPv4
+ * and IPv6 addresses, it will attempt to connect to each of a host's addresses
+ * until a connection is established.
+ *
+ *
Response Caching
+ * Android 4.0 (Ice Cream Sandwich) includes a response cache. See {@code
+ * android.net.http.HttpResponseCache} for instructions on enabling HTTP caching
+ * in your application.
+ *
+ *
Avoiding Bugs In Earlier Releases
+ * Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In
+ * particular, calling {@code close()} on a readable {@code InputStream} could
+ * poison the
+ * connection pool. Work around this by disabling connection pooling:
+ *
{@code
+ * private void disableConnectionReuseIfNecessary() {
+ * // Work around pre-Froyo bugs in HTTP connection reuse.
+ * if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
+ * System.setProperty("http.keepAlive", "false");
+ * }
+ * }}
+ *
+ *
Each instance of {@code HttpURLConnection} may be used for one
+ * request/response pair. Instances of this class are not thread safe.
+ */
+public abstract class OkHttpConnection extends URLConnection {
+
+ /**
+ * The subset of HTTP methods that the user may select via {@link
+ * #setRequestMethod(String)}.
+ */
+ private static final String[] PERMITTED_USER_METHODS = {
+ HttpEngine.OPTIONS,
+ HttpEngine.GET,
+ HttpEngine.HEAD,
+ HttpEngine.POST,
+ HttpEngine.PUT,
+ HttpEngine.DELETE,
+ HttpEngine.TRACE
+ // Note: we don't allow users to specify "CONNECT"
+ };
+
+ /**
+ * The HTTP request method of this {@code HttpURLConnection}. The default
+ * value is {@code "GET"}.
+ */
+ protected String method = HttpEngine.GET;
+
+ /**
+ * The status code of the response obtained from the HTTP request. The
+ * default value is {@code -1}.
+ *
+ *
1xx: Informational
+ *
2xx: Success
+ *
3xx: Relocation/Redirection
+ *
4xx: Client Error
+ *
5xx: Server Error
+ */
+ protected int responseCode = -1;
+
+ /**
+ * The HTTP response message which corresponds to the response code.
+ */
+ protected String responseMessage;
+
+ /**
+ * Flag to define whether the protocol will automatically follow redirects
+ * or not. The default value is {@code true}.
+ */
+ protected boolean instanceFollowRedirects = followRedirects;
+
+ private static boolean followRedirects = true;
+
+ /**
+ * If the HTTP chunked encoding is enabled this parameter defines the
+ * chunk-length. Default value is {@code -1} that means the chunked encoding
+ * mode is disabled.
+ */
+ protected int chunkLength = -1;
+
+ /**
+ * If using HTTP fixed-length streaming mode this parameter defines the
+ * fixed length of content. Default value is {@code -1} that means the
+ * fixed-length streaming mode is disabled.
+ */
+ protected int fixedContentLength = -1;
+
+ // 2XX: generally "OK"
+ // 3XX: relocation/redirect
+ // 4XX: client error
+ // 5XX: server error
+ /**
+ * Numeric status code, 202: Accepted
+ */
+ public static final int HTTP_ACCEPTED = 202;
+
+ /**
+ * Numeric status code, 502: Bad Gateway
+ */
+ public static final int HTTP_BAD_GATEWAY = 502;
+
+ /**
+ * Numeric status code, 405: Bad Method
+ */
+ public static final int HTTP_BAD_METHOD = 405;
+
+ /**
+ * Numeric status code, 400: Bad Request
+ */
+ public static final int HTTP_BAD_REQUEST = 400;
+
+ /**
+ * Numeric status code, 408: Client Timeout
+ */
+ public static final int HTTP_CLIENT_TIMEOUT = 408;
+
+ /**
+ * Numeric status code, 409: Conflict
+ */
+ public static final int HTTP_CONFLICT = 409;
+
+ /**
+ * Numeric status code, 201: Created
+ */
+ public static final int HTTP_CREATED = 201;
+
+ /**
+ * Numeric status code, 413: Entity too large
+ */
+ public static final int HTTP_ENTITY_TOO_LARGE = 413;
+
+ /**
+ * Numeric status code, 403: Forbidden
+ */
+ public static final int HTTP_FORBIDDEN = 403;
+
+ /**
+ * Numeric status code, 504: Gateway timeout
+ */
+ public static final int HTTP_GATEWAY_TIMEOUT = 504;
+
+ /**
+ * Numeric status code, 410: Gone
+ */
+ public static final int HTTP_GONE = 410;
+
+ /**
+ * Numeric status code, 500: Internal error
+ */
+ public static final int HTTP_INTERNAL_ERROR = 500;
+
+ /**
+ * Numeric status code, 411: Length required
+ */
+ public static final int HTTP_LENGTH_REQUIRED = 411;
+
+ /**
+ * Numeric status code, 301 Moved permanently
+ */
+ public static final int HTTP_MOVED_PERM = 301;
+
+ /**
+ * Numeric status code, 302: Moved temporarily
+ */
+ public static final int HTTP_MOVED_TEMP = 302;
+
+ /**
+ * Numeric status code, 300: Multiple choices
+ */
+ public static final int HTTP_MULT_CHOICE = 300;
+
+ /**
+ * Numeric status code, 204: No content
+ */
+ public static final int HTTP_NO_CONTENT = 204;
+
+ /**
+ * Numeric status code, 406: Not acceptable
+ */
+ public static final int HTTP_NOT_ACCEPTABLE = 406;
+
+ /**
+ * Numeric status code, 203: Not authoritative
+ */
+ public static final int HTTP_NOT_AUTHORITATIVE = 203;
+
+ /**
+ * Numeric status code, 404: Not found
+ */
+ public static final int HTTP_NOT_FOUND = 404;
+
+ /**
+ * Numeric status code, 501: Not implemented
+ */
+ public static final int HTTP_NOT_IMPLEMENTED = 501;
+
+ /**
+ * Numeric status code, 304: Not modified
+ */
+ public static final int HTTP_NOT_MODIFIED = 304;
+
+ /**
+ * Numeric status code, 200: OK
+ */
+ public static final int HTTP_OK = 200;
+
+ /**
+ * Numeric status code, 206: Partial
+ */
+ public static final int HTTP_PARTIAL = 206;
+
+ /**
+ * Numeric status code, 402: Payment required
+ */
+ public static final int HTTP_PAYMENT_REQUIRED = 402;
+
+ /**
+ * Numeric status code, 412: Precondition failed
+ */
+ public static final int HTTP_PRECON_FAILED = 412;
+
+ /**
+ * Numeric status code, 407: Proxy authentication required
+ */
+ public static final int HTTP_PROXY_AUTH = 407;
+
+ /**
+ * Numeric status code, 414: Request too long
+ */
+ public static final int HTTP_REQ_TOO_LONG = 414;
+
+ /**
+ * Numeric status code, 205: Reset
+ */
+ public static final int HTTP_RESET = 205;
+
+ /**
+ * Numeric status code, 303: See other
+ */
+ public static final int HTTP_SEE_OTHER = 303;
+
+ /**
+ * Numeric status code, 500: Internal error
+ *
+ * @deprecated Use {@link #HTTP_INTERNAL_ERROR}
+ */
+ @Deprecated
+ public static final int HTTP_SERVER_ERROR = 500;
+
+ /**
+ * Numeric status code, 305: Use proxy.
+ *
+ *
Like Firefox and Chrome, this class doesn't honor this response code.
+ * Other implementations respond to this status code by retrying the request
+ * using the HTTP proxy named by the response's Location header field.
+ */
+ public static final int HTTP_USE_PROXY = 305;
+
+ /**
+ * Numeric status code, 401: Unauthorized
+ */
+ public static final int HTTP_UNAUTHORIZED = 401;
+
+ /**
+ * Numeric status code, 415: Unsupported type
+ */
+ public static final int HTTP_UNSUPPORTED_TYPE = 415;
+
+ /**
+ * Numeric status code, 503: Unavailable
+ */
+ public static final int HTTP_UNAVAILABLE = 503;
+
+ /**
+ * Numeric status code, 505: Version not supported
+ */
+ public static final int HTTP_VERSION = 505;
+
+ public static OkHttpConnection open(URL url) {
+ return new libcore.net.http.HttpURLConnectionImpl(url, 443);
+ }
+
+ public static OkHttpConnection open(URL url, Proxy proxy) {
+ return new libcore.net.http.HttpURLConnectionImpl(url, 443, proxy);
+ }
+
+ /**
+ * Constructs a new {@code HttpURLConnection} instance pointing to the
+ * resource specified by the {@code url}.
+ *
+ * @param url
+ * the URL of this connection.
+ * @see java.net.URL
+ * @see java.net.URLConnection
+ */
+ protected OkHttpConnection(URL url) {
+ super(url);
+ }
+
+ /**
+ * Releases this connection so that its resources may be either reused or
+ * closed.
+ *
+ *
Unlike other Java implementations, this will not necessarily close
+ * socket connections that can be reused. You can disable all connection
+ * reuse by setting the {@code http.keepAlive} system property to {@code
+ * false} before issuing any HTTP requests.
+ */
+ public abstract void disconnect();
+
+ /**
+ * Returns an input stream from the server in the case of an error such as
+ * the requested file has not been found on the remote server. This stream
+ * can be used to read the data the server will send back.
+ *
+ * @return the error input stream returned by the server.
+ */
+ public InputStream getErrorStream() {
+ return null;
+ }
+
+ /**
+ * Returns the value of {@code followRedirects} which indicates if this
+ * connection follows a different URL redirected by the server. It is
+ * enabled by default.
+ *
+ * @return the value of the flag.
+ * @see #setFollowRedirects
+ */
+ public static boolean getFollowRedirects() {
+ return followRedirects;
+ }
+
+ /**
+ * Returns the permission object (in this case {@code SocketPermission})
+ * with the host and the port number as the target name and {@code
+ * "resolve, connect"} as the action list. If the port number of this URL
+ * instance is lower than {@code 0} the port will be set to {@code 80}.
+ *
+ * @return the permission object required for this connection.
+ * @throws java.io.IOException
+ * if an IO exception occurs during the creation of the
+ * permission object.
+ */
+ @Override
+ public java.security.Permission getPermission() throws IOException {
+ int port = url.getPort();
+ if (port < 0) {
+ port = 80;
+ }
+ return new SocketPermission(url.getHost() + ":" + port,
+ "connect, resolve");
+ }
+
+ /**
+ * Returns the request method which will be used to make the request to the
+ * remote HTTP server. All possible methods of this HTTP implementation is
+ * listed in the class definition.
+ *
+ * @return the request method string.
+ * @see #method
+ * @see #setRequestMethod
+ */
+ public String getRequestMethod() {
+ return method;
+ }
+
+ /**
+ * Returns the response code returned by the remote HTTP server.
+ *
+ * @return the response code, -1 if no valid response code.
+ * @throws java.io.IOException
+ * if there is an IO error during the retrieval.
+ * @see #getResponseMessage
+ */
+ public int getResponseCode() throws IOException {
+ // Call getInputStream() first since getHeaderField() doesn't return
+ // exceptions
+ getInputStream();
+ String response = getHeaderField(0);
+ if (response == null) {
+ return -1;
+ }
+ response = response.trim();
+ int mark = response.indexOf(" ") + 1;
+ if (mark == 0) {
+ return -1;
+ }
+ int last = mark + 3;
+ if (last > response.length()) {
+ last = response.length();
+ }
+ responseCode = Integer.parseInt(response.substring(mark, last));
+ if (last + 1 <= response.length()) {
+ responseMessage = response.substring(last + 1);
+ }
+ return responseCode;
+ }
+
+ /**
+ * Returns the response message returned by the remote HTTP server.
+ *
+ * @return the response message. {@code null} if no such response exists.
+ * @throws java.io.IOException
+ * if there is an error during the retrieval.
+ * @see #getResponseCode()
+ */
+ public String getResponseMessage() throws IOException {
+ if (responseMessage != null) {
+ return responseMessage;
+ }
+ getResponseCode();
+ return responseMessage;
+ }
+
+ /**
+ * Sets the flag of whether this connection will follow redirects returned
+ * by the remote server.
+ *
+ * @param auto
+ * the value to enable or disable this option.
+ */
+ public static void setFollowRedirects(boolean auto) {
+ followRedirects = auto;
+ }
+
+ /**
+ * Sets the request command which will be sent to the remote HTTP server.
+ * This method can only be called before the connection is made.
+ *
+ * @param method
+ * the string representing the method to be used.
+ * @throws java.net.ProtocolException
+ * if this is called after connected, or the method is not
+ * supported by this HTTP implementation.
+ * @see #getRequestMethod()
+ * @see #method
+ */
+ public void setRequestMethod(String method) throws ProtocolException {
+ if (connected) {
+ throw new ProtocolException("Connection already established");
+ }
+ for (String permittedUserMethod : PERMITTED_USER_METHODS) {
+ if (permittedUserMethod.equals(method)) {
+ // if there is a supported method that matches the desired
+ // method, then set the current method and return
+ this.method = permittedUserMethod;
+ return;
+ }
+ }
+ // if none matches, then throw ProtocolException
+ throw new ProtocolException("Unknown method '" + method + "'; must be one of " +
+ Arrays.toString(PERMITTED_USER_METHODS));
+ }
+
+ /**
+ * Returns whether this connection uses a proxy server or not.
+ *
+ * @return {@code true} if this connection passes a proxy server, false
+ * otherwise.
+ */
+ public abstract boolean usingProxy();
+
+ /**
+ * Returns the encoding used to transmit the response body over the network.
+ * This is null or "identity" if the content was not encoded, or "gzip" if
+ * the body was gzip compressed. Most callers will be more interested in the
+ * {@link #getContentType() content type}, which may also include the
+ * content's character encoding.
+ */
+ @Override public String getContentEncoding() {
+ return super.getContentEncoding(); // overridden for Javadoc only
+ }
+
+ /**
+ * Returns whether this connection follows redirects.
+ *
+ * @return {@code true} if this connection follows redirects, false
+ * otherwise.
+ */
+ public boolean getInstanceFollowRedirects() {
+ return instanceFollowRedirects;
+ }
+
+ /**
+ * Sets whether this connection follows redirects.
+ *
+ * @param followRedirects
+ * {@code true} if this connection will follows redirects, false
+ * otherwise.
+ */
+ public void setInstanceFollowRedirects(boolean followRedirects) {
+ instanceFollowRedirects = followRedirects;
+ }
+
+ /**
+ * Returns the date value in milliseconds since {@code 01.01.1970, 00:00h}
+ * corresponding to the header field {@code field}. The {@code defaultValue}
+ * will be returned if no such field can be found in the response header.
+ *
+ * @param field
+ * the header field name.
+ * @param defaultValue
+ * the default value to use if the specified header field wont be
+ * found.
+ * @return the header field represented in milliseconds since January 1,
+ * 1970 GMT.
+ */
+ @Override
+ public long getHeaderFieldDate(String field, long defaultValue) {
+ return super.getHeaderFieldDate(field, defaultValue);
+ }
+
+ /**
+ * If the length of a HTTP request body is known ahead, sets fixed length to
+ * enable streaming without buffering. Sets after connection will cause an
+ * exception.
+ *
+ * @see #setChunkedStreamingMode
+ * @param contentLength
+ * the fixed length of the HTTP request body.
+ * @throws IllegalStateException
+ * if already connected or another mode already set.
+ * @throws IllegalArgumentException
+ * if {@code contentLength} is less than zero.
+ */
+ public void setFixedLengthStreamingMode(int contentLength) {
+ if (super.connected) {
+ throw new IllegalStateException("Already connected");
+ }
+ if (chunkLength > 0) {
+ throw new IllegalStateException("Already in chunked mode");
+ }
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("contentLength < 0");
+ }
+ this.fixedContentLength = contentLength;
+ }
+
+ /**
+ * Stream a request body whose length is not known in advance. Old HTTP/1.0
+ * only servers may not support this mode.
+ *
+ *
When HTTP chunked encoding is used, the stream is divided into
+ * chunks, each prefixed with a header containing the chunk's size. Setting
+ * a large chunk length requires a large internal buffer, potentially
+ * wasting memory. Setting a small chunk length increases the number of
+ * bytes that must be transmitted because of the header on every chunk.
+ * Most caller should use {@code 0} to get the system default.
+ *
+ * @see #setFixedLengthStreamingMode
+ * @param chunkLength the length to use, or {@code 0} for the default chunk
+ * length.
+ * @throws IllegalStateException if already connected or another mode
+ * already set.
+ */
+ public void setChunkedStreamingMode(int chunkLength) {
+ if (super.connected) {
+ throw new IllegalStateException("Already connected");
+ }
+ if (fixedContentLength >= 0) {
+ throw new IllegalStateException("Already in fixed-length mode");
+ }
+ if (chunkLength <= 0) {
+ this.chunkLength = HttpEngine.DEFAULT_CHUNK_LENGTH;
+ } else {
+ this.chunkLength = chunkLength;
+ }
+ }
+}
diff --git a/src/main/java/com/squareup/okhttp/OkHttpsConnection.java b/src/main/java/com/squareup/okhttp/OkHttpsConnection.java
new file mode 100644
index 000000000000..7c7540c325eb
--- /dev/null
+++ b/src/main/java/com/squareup/okhttp/OkHttpsConnection.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.squareup.okhttp;
+
+import java.net.Proxy;
+import java.net.URL;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * An {@link java.net.HttpURLConnection} for HTTPS (RFC 2818). A
+ * connected {@code HttpsURLConnection} allows access to the
+ * negotiated cipher suite, the server certificate chain, and the
+ * client certificate chain if any.
+ *
+ *
Providing an application specific X509TrustManager
+ *
+ * If an application wants to trust Certificate Authority (CA)
+ * certificates that are not part of the system, it should specify its
+ * own {@code X509TrustManager} via a {@code SSLSocketFactory} set on
+ * the {@code HttpsURLConnection}. The {@code X509TrustManager} can be
+ * created based on a {@code KeyStore} using a {@code
+ * TrustManagerFactory} to supply trusted CA certificates. Note that
+ * self-signed certificates are effectively their own CA and can be
+ * trusted by including them in a {@code KeyStore}.
+ *
+ *
For example, to trust a set of certificates specified by a {@code KeyStore}:
+ *
It is possible to implement {@code X509TrustManager} directly
+ * instead of using one created by a {@code
+ * TrustManagerFactory}. While this is straightforward in the insecure
+ * case of allowing all certificate chains to pass verification,
+ * writing a proper implementation will usually want to take advantage
+ * of {@link java.security.cert.CertPathValidator
+ * CertPathValidator}. In general, it might be better to write a
+ * custom {@code KeyStore} implementation to pass to the {@code
+ * TrustManagerFactory} than to try and write a custom {@code
+ * X509TrustManager}.
+ *
+ *
Providing an application specific X509KeyManager
+ *
+ * A custom {@code X509KeyManager} can be used to supply a client
+ * certificate and its associated private key to authenticate a
+ * connection to the server. The {@code X509KeyManager} can be created
+ * based on a {@code KeyStore} using a {@code KeyManagerFactory}.
+ *
+ *
For example, to supply client certificates from a {@code KeyStore}:
+ *
A {@code X509KeyManager} can also be implemented directly. This
+ * can allow an application to return a certificate and private key
+ * from a non-{@code KeyStore} source or to specify its own logic for
+ * selecting a specific credential to use when many may be present in
+ * a single {@code KeyStore}.
+ *
+ *
TLS Intolerance Support
+ *
+ * This class attempts to create secure connections using common TLS
+ * extensions and SSL deflate compression. Should that fail, the
+ * connection will be retried with SSLv3 only.
+ */
+public abstract class OkHttpsConnection extends OkHttpConnection {
+
+ private static HostnameVerifier defaultHostnameVerifier
+ = HttpsURLConnection.getDefaultHostnameVerifier();
+
+ private static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory
+ .getDefault();
+
+ public static OkHttpsConnection open(URL url) {
+ return new libcore.net.http.HttpsURLConnectionImpl(url, 443);
+ }
+
+ public static OkHttpsConnection open(URL url, Proxy proxy) {
+ return new libcore.net.http.HttpsURLConnectionImpl(url, 443, proxy);
+ }
+
+ /**
+ * Sets the default hostname verifier to be used by new instances.
+ *
+ * @param v
+ * the new default hostname verifier
+ * @throws IllegalArgumentException
+ * if the specified verifier is {@code null}.
+ */
+ public static void setDefaultHostnameVerifier(HostnameVerifier v) {
+ if (v == null) {
+ throw new IllegalArgumentException("HostnameVerifier is null");
+ }
+ defaultHostnameVerifier = v;
+ }
+
+ /**
+ * Returns the default hostname verifier.
+ *
+ * @return the default hostname verifier.
+ */
+ public static HostnameVerifier getDefaultHostnameVerifier() {
+ return defaultHostnameVerifier;
+ }
+
+ /**
+ * Sets the default SSL socket factory to be used by new instances.
+ *
+ * @param sf
+ * the new default SSL socket factory.
+ * @throws IllegalArgumentException
+ * if the specified socket factory is {@code null}.
+ */
+ public static void setDefaultSSLSocketFactory(SSLSocketFactory sf) {
+ if (sf == null) {
+ throw new IllegalArgumentException("SSLSocketFactory is null");
+ }
+ defaultSSLSocketFactory = sf;
+ }
+
+ /**
+ * Returns the default SSL socket factory for new instances.
+ *
+ * @return the default SSL socket factory for new instances.
+ */
+ public static SSLSocketFactory getDefaultSSLSocketFactory() {
+ return defaultSSLSocketFactory;
+ }
+
+ /**
+ * The host name verifier used by this connection. It is initialized from
+ * the default hostname verifier
+ * {@link #setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier)} or
+ * {@link #getDefaultHostnameVerifier()}.
+ */
+ protected HostnameVerifier hostnameVerifier;
+
+ private SSLSocketFactory sslSocketFactory;
+
+ /**
+ * Creates a new {@code HttpsURLConnection} with the specified {@code URL}.
+ *
+ * @param url
+ * the {@code URL} to connect to.
+ */
+ protected OkHttpsConnection(URL url) {
+ super(url);
+ hostnameVerifier = defaultHostnameVerifier;
+ sslSocketFactory = defaultSSLSocketFactory;
+ }
+
+ /**
+ * Returns the name of the cipher suite negotiated during the SSL handshake.
+ *
+ * @return the name of the cipher suite negotiated during the SSL handshake.
+ * @throws IllegalStateException
+ * if no connection has been established yet.
+ */
+ public abstract String getCipherSuite();
+
+ /**
+ * Returns the list of local certificates used during the handshake. These
+ * certificates were sent to the peer.
+ *
+ * @return Returns the list of certificates used during the handshake with
+ * the local identity certificate followed by CAs, or {@code null}
+ * if no certificates were used during the handshake.
+ * @throws IllegalStateException
+ * if no connection has been established yet.
+ */
+ public abstract Certificate[] getLocalCertificates();
+
+ /**
+ * Return the list of certificates identifying the peer during the
+ * handshake.
+ *
+ * @return the list of certificates identifying the peer with the peer's
+ * identity certificate followed by CAs.
+ * @throws javax.net.ssl.SSLPeerUnverifiedException
+ * if the identity of the peer has not been verified..
+ * @throws IllegalStateException
+ * if no connection has been established yet.
+ */
+ public abstract Certificate[] getServerCertificates() throws SSLPeerUnverifiedException;
+
+ /**
+ * Returns the {@code Principal} identifying the peer.
+ *
+ * @return the {@code Principal} identifying the peer.
+ * @throws javax.net.ssl.SSLPeerUnverifiedException
+ * if the identity of the peer has not been verified.
+ * @throws IllegalStateException
+ * if no connection has been established yet.
+ */
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ Certificate[] certs = getServerCertificates();
+ if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) {
+ throw new SSLPeerUnverifiedException("No server's end-entity certificate");
+ }
+ return ((X509Certificate) certs[0]).getSubjectX500Principal();
+ }
+
+ /**
+ * Returns the {@code Principal} used to identify the local host during the handshake.
+ *
+ * @return the {@code Principal} used to identify the local host during the handshake, or
+ * {@code null} if none was used.
+ * @throws IllegalStateException
+ * if no connection has been established yet.
+ */
+ public Principal getLocalPrincipal() {
+ Certificate[] certs = getLocalCertificates();
+ if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) {
+ return null;
+ }
+ return ((X509Certificate) certs[0]).getSubjectX500Principal();
+ }
+
+ /**
+ * Sets the hostname verifier for this instance.
+ *
+ * @param v
+ * the hostname verifier for this instance.
+ * @throws IllegalArgumentException
+ * if the specified verifier is {@code null}.
+ */
+ public void setHostnameVerifier(HostnameVerifier v) {
+ if (v == null) {
+ throw new IllegalArgumentException("HostnameVerifier is null");
+ }
+ hostnameVerifier = v;
+ }
+
+ /**
+ * Returns the hostname verifier used by this instance.
+ *
+ * @return the hostname verifier used by this instance.
+ */
+ public HostnameVerifier getHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+ /**
+ * Sets the SSL socket factory for this instance.
+ *
+ * @param sf
+ * the SSL socket factory to be used by this instance.
+ * @throws IllegalArgumentException
+ * if the specified socket factory is {@code null}.
+ */
+ public void setSSLSocketFactory(SSLSocketFactory sf) {
+ if (sf == null) {
+ throw new IllegalArgumentException("SSLSocketFactory is null");
+ }
+ sslSocketFactory = sf;
+ }
+
+ /**
+ * Returns the SSL socket factory used by this instance.
+ *
+ * @return the SSL socket factory used by this instance.
+ */
+ public SSLSocketFactory getSSLSocketFactory() {
+ return sslSocketFactory;
+ }
+
+}
diff --git a/src/main/java/libcore/io/AsynchronousCloseMonitor.java b/src/main/java/libcore/io/AsynchronousCloseMonitor.java
new file mode 100644
index 000000000000..62eec24af9bc
--- /dev/null
+++ b/src/main/java/libcore/io/AsynchronousCloseMonitor.java
@@ -0,0 +1,26 @@
+/*
+ * 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 libcore.io;
+
+import java.io.FileDescriptor;
+
+public final class AsynchronousCloseMonitor {
+ private AsynchronousCloseMonitor() {
+ }
+
+ public static native void signalBlockedThreads(FileDescriptor fd);
+}
diff --git a/src/main/java/libcore/io/Base64.java b/src/main/java/libcore/io/Base64.java
new file mode 100644
index 000000000000..153722192c1e
--- /dev/null
+++ b/src/main/java/libcore/io/Base64.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+* @author Alexander Y. Kleymenov
+*/
+
+package libcore.io;
+
+import libcore.util.Charsets;
+import libcore.util.EmptyArray;
+
+/**
+ * Base64 encoder/decoder.
+ * In violation of the RFC, this encoder doesn't wrap lines at 76 columns.
+ */
+public final class Base64 {
+ private Base64() {
+ }
+
+ public static byte[] decode(byte[] in) {
+ return decode(in, in.length);
+ }
+
+ public static byte[] decode(byte[] in, int len) {
+ // approximate output length
+ int length = len / 4 * 3;
+ // return an empty array on empty or short input without padding
+ if (length == 0) {
+ return EmptyArray.BYTE;
+ }
+ // temporary array
+ byte[] out = new byte[length];
+ // number of padding characters ('=')
+ int pad = 0;
+ byte chr;
+ // compute the number of the padding characters
+ // and adjust the length of the input
+ for (;;len--) {
+ chr = in[len-1];
+ // skip the neutral characters
+ if ((chr == '\n') || (chr == '\r') ||
+ (chr == ' ') || (chr == '\t')) {
+ continue;
+ }
+ if (chr == '=') {
+ pad++;
+ } else {
+ break;
+ }
+ }
+ // index in the output array
+ int outIndex = 0;
+ // index in the input array
+ int inIndex = 0;
+ // holds the value of the input character
+ int bits = 0;
+ // holds the value of the input quantum
+ int quantum = 0;
+ for (int i=0; i= 'A') && (chr <= 'Z')) {
+ // char ASCII value
+ // A 65 0
+ // Z 90 25 (ASCII - 65)
+ bits = chr - 65;
+ } else if ((chr >= 'a') && (chr <= 'z')) {
+ // char ASCII value
+ // a 97 26
+ // z 122 51 (ASCII - 71)
+ bits = chr - 71;
+ } else if ((chr >= '0') && (chr <= '9')) {
+ // char ASCII value
+ // 0 48 52
+ // 9 57 61 (ASCII + 4)
+ bits = chr + 4;
+ } else if (chr == '+') {
+ bits = 62;
+ } else if (chr == '/') {
+ bits = 63;
+ } else {
+ return null;
+ }
+ // append the value to the quantum
+ quantum = (quantum << 6) | (byte) bits;
+ if (inIndex%4 == 3) {
+ // 4 characters were read, so make the output:
+ out[outIndex++] = (byte) (quantum >> 16);
+ out[outIndex++] = (byte) (quantum >> 8);
+ out[outIndex++] = (byte) quantum;
+ }
+ inIndex++;
+ }
+ if (pad > 0) {
+ // adjust the quantum value according to the padding
+ quantum = quantum << (6*pad);
+ // make output
+ out[outIndex++] = (byte) (quantum >> 16);
+ if (pad == 1) {
+ out[outIndex++] = (byte) (quantum >> 8);
+ }
+ }
+ // create the resulting array
+ byte[] result = new byte[outIndex];
+ System.arraycopy(out, 0, result, 0, outIndex);
+ return result;
+ }
+
+ private static final byte[] map = new byte[]
+ {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
+ 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'};
+
+ public static String encode(byte[] in) {
+ int length = (in.length + 2) * 4 / 3;
+ byte[] out = new byte[length];
+ int index = 0, end = in.length - in.length % 3;
+ for (int i = 0; i < end; i += 3) {
+ out[index++] = map[(in[i] & 0xff) >> 2];
+ out[index++] = map[((in[i] & 0x03) << 4) | ((in[i+1] & 0xff) >> 4)];
+ out[index++] = map[((in[i+1] & 0x0f) << 2) | ((in[i+2] & 0xff) >> 6)];
+ out[index++] = map[(in[i+2] & 0x3f)];
+ }
+ switch (in.length % 3) {
+ case 1:
+ out[index++] = map[(in[end] & 0xff) >> 2];
+ out[index++] = map[(in[end] & 0x03) << 4];
+ out[index++] = '=';
+ out[index++] = '=';
+ break;
+ case 2:
+ out[index++] = map[(in[end] & 0xff) >> 2];
+ out[index++] = map[((in[end] & 0x03) << 4) | ((in[end+1] & 0xff) >> 4)];
+ out[index++] = map[((in[end+1] & 0x0f) << 2)];
+ out[index++] = '=';
+ break;
+ }
+ return new String(out, 0, index, Charsets.US_ASCII);
+ }
+}
diff --git a/src/main/java/libcore/io/BufferIterator.java b/src/main/java/libcore/io/BufferIterator.java
new file mode 100644
index 000000000000..7f3ad472bcd0
--- /dev/null
+++ b/src/main/java/libcore/io/BufferIterator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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 libcore.io;
+
+/**
+ * Iterates over big- or little-endian bytes. See {@link MemoryMappedFile#bigEndianIterator} and
+ * {@link MemoryMappedFile#littleEndianIterator}.
+ *
+ * @hide don't make this public without adding bounds checking.
+ */
+public abstract class BufferIterator {
+ /**
+ * Seeks to the absolute position {@code offset}, measured in bytes from the start.
+ */
+ public abstract void seek(int offset);
+
+ /**
+ * Skips forwards or backwards {@code byteCount} bytes from the current position.
+ */
+ public abstract void skip(int byteCount);
+
+ /**
+ * Copies {@code byteCount} bytes from the current position into {@code dst}, starting at
+ * {@code dstOffset}, and advances the current position {@code byteCount} bytes.
+ */
+ public abstract void readByteArray(byte[] dst, int dstOffset, int byteCount);
+
+ /**
+ * Returns the byte at the current position, and advances the current position one byte.
+ */
+ public abstract byte readByte();
+
+ /**
+ * Returns the 32-bit int at the current position, and advances the current position four bytes.
+ */
+ public abstract int readInt();
+
+ /**
+ * Copies {@code intCount} 32-bit ints from the current position into {@code dst}, starting at
+ * {@code dstOffset}, and advances the current position {@code 4 * intCount} bytes.
+ */
+ public abstract void readIntArray(int[] dst, int dstOffset, int intCount);
+
+ /**
+ * Returns the 16-bit short at the current position, and advances the current position two bytes.
+ */
+ public abstract short readShort();
+}
diff --git a/src/main/java/libcore/io/DiskLruCache.java b/src/main/java/libcore/io/DiskLruCache.java
new file mode 100644
index 000000000000..b6c3638cdeb0
--- /dev/null
+++ b/src/main/java/libcore/io/DiskLruCache.java
@@ -0,0 +1,834 @@
+/*
+ * 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 libcore.io;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import libcore.util.Charsets;
+import libcore.util.Libcore;
+
+/**
+ * A cache that uses a bounded amount of space on a filesystem. Each cache
+ * entry has a string key and a fixed number of values. Values are byte
+ * sequences, accessible as streams or files. Each value must be between {@code
+ * 0} and {@code Integer.MAX_VALUE} bytes in length.
+ *
+ *
The cache stores its data in a directory on the filesystem. This
+ * directory must be exclusive to the cache; the cache may delete or overwrite
+ * files from its directory. It is an error for multiple processes to use the
+ * same cache directory at the same time.
+ *
+ *
This cache limits the number of bytes that it will store on the
+ * filesystem. When the number of stored bytes exceeds the limit, the cache will
+ * remove entries in the background until the limit is satisfied. The limit is
+ * not strict: the cache may temporarily exceed it while waiting for files to be
+ * deleted. The limit does not include filesystem overhead or the cache
+ * journal so space-sensitive applications should set a conservative limit.
+ *
+ *
Clients call {@link #edit} to create or update the values of an entry. An
+ * entry may have only one editor at one time; if a value is not available to be
+ * edited then {@link #edit} will return null.
+ *
+ *
When an entry is being created it is necessary to
+ * supply a full set of values; the empty value should be used as a
+ * placeholder if necessary.
+ *
When an entry is being edited, it is not necessary
+ * to supply data for every value; values default to their previous
+ * value.
+ *
+ * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
+ * or {@link Editor#abort}. Committing is atomic: a read observes the full set
+ * of values as they were before or after the commit, but never a mix of values.
+ *
+ *
Clients call {@link #get} to read a snapshot of an entry. The read will
+ * observe the value at the time that {@link #get} was called. Updates and
+ * removals after the call do not impact ongoing reads.
+ *
+ *
This class is tolerant of some I/O errors. If files are missing from the
+ * filesystem, the corresponding entries will be dropped from the cache. If
+ * an error occurs while writing a cache value, the edit will fail silently.
+ * Callers should handle other problems by catching {@code IOException} and
+ * responding appropriately.
+ */
+public final class DiskLruCache implements Closeable {
+ static final String JOURNAL_FILE = "journal";
+ static final String JOURNAL_FILE_TMP = "journal.tmp";
+ static final String MAGIC = "libcore.io.DiskLruCache";
+ static final String VERSION_1 = "1";
+ static final long ANY_SEQUENCE_NUMBER = -1;
+ private static final String CLEAN = "CLEAN";
+ private static final String DIRTY = "DIRTY";
+ private static final String REMOVE = "REMOVE";
+ private static final String READ = "READ";
+
+ /*
+ * This cache uses a journal file named "journal". A typical journal file
+ * looks like this:
+ * libcore.io.DiskLruCache
+ * 1
+ * 100
+ * 2
+ *
+ * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
+ * DIRTY 335c4c6028171cfddfbaae1a9c313c52
+ * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
+ * REMOVE 335c4c6028171cfddfbaae1a9c313c52
+ * DIRTY 1ab96a171faeeee38496d8b330771a7a
+ * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
+ * READ 335c4c6028171cfddfbaae1a9c313c52
+ * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
+ *
+ * The first five lines of the journal form its header. They are the
+ * constant string "libcore.io.DiskLruCache", the disk cache's version,
+ * the application's version, the value count, and a blank line.
+ *
+ * Each of the subsequent lines in the file is a record of the state of a
+ * cache entry. Each line contains space-separated values: a state, a key,
+ * and optional state-specific values.
+ * o DIRTY lines track that an entry is actively being created or updated.
+ * Every successful DIRTY action should be followed by a CLEAN or REMOVE
+ * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
+ * temporary files may need to be deleted.
+ * o CLEAN lines track a cache entry that has been successfully published
+ * and may be read. A publish line is followed by the lengths of each of
+ * its values.
+ * o READ lines track accesses for LRU.
+ * o REMOVE lines track entries that have been deleted.
+ *
+ * The journal file is appended to as cache operations occur. The journal may
+ * occasionally be compacted by dropping redundant lines. A temporary file named
+ * "journal.tmp" will be used during compaction; that file should be deleted if
+ * it exists when the cache is opened.
+ */
+
+ private final File directory;
+ private final File journalFile;
+ private final File journalFileTmp;
+ private final int appVersion;
+ private final long maxSize;
+ private final int valueCount;
+ private long size = 0;
+ private Writer journalWriter;
+ private final LinkedHashMap lruEntries
+ = new LinkedHashMap(0, 0.75f, true);
+ private int redundantOpCount;
+
+ /**
+ * To differentiate between old and current snapshots, each entry is given
+ * a sequence number each time an edit is committed. A snapshot is stale if
+ * its sequence number is not equal to its entry's sequence number.
+ */
+ private long nextSequenceNumber = 0;
+
+ /** This cache uses a single background thread to evict entries. */
+ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
+ 60L, TimeUnit.SECONDS, new LinkedBlockingQueue());
+ private final Callable cleanupCallable = new Callable() {
+ @Override public Void call() throws Exception {
+ synchronized (DiskLruCache.this) {
+ if (journalWriter == null) {
+ return null; // closed
+ }
+ trimToSize();
+ if (journalRebuildRequired()) {
+ rebuildJournal();
+ redundantOpCount = 0;
+ }
+ }
+ return null;
+ }
+ };
+
+ private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
+ this.directory = directory;
+ this.appVersion = appVersion;
+ this.journalFile = new File(directory, JOURNAL_FILE);
+ this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
+ this.valueCount = valueCount;
+ this.maxSize = maxSize;
+ }
+
+ /**
+ * Opens the cache in {@code directory}, creating a cache if none exists
+ * there.
+ *
+ * @param directory a writable directory
+ * @param appVersion
+ * @param valueCount the number of values per cache entry. Must be positive.
+ * @param maxSize the maximum number of bytes this cache should use to store
+ * @throws IOException if reading or writing the cache directory fails
+ */
+ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
+ throws IOException {
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("maxSize <= 0");
+ }
+ if (valueCount <= 0) {
+ throw new IllegalArgumentException("valueCount <= 0");
+ }
+
+ // prefer to pick up where we left off
+ DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ if (cache.journalFile.exists()) {
+ try {
+ cache.readJournal();
+ cache.processJournal();
+ cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true));
+ return cache;
+ } catch (IOException journalIsCorrupt) {
+ Libcore.logW("DiskLruCache " + directory + " is corrupt: "
+ + journalIsCorrupt.getMessage() + ", removing");
+ cache.delete();
+ }
+ }
+
+ // create a new empty cache
+ directory.mkdirs();
+ cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ cache.rebuildJournal();
+ return cache;
+ }
+
+ private void readJournal() throws IOException {
+ InputStream in = new BufferedInputStream(new FileInputStream(journalFile));
+ try {
+ String magic = Streams.readAsciiLine(in);
+ String version = Streams.readAsciiLine(in);
+ String appVersionString = Streams.readAsciiLine(in);
+ String valueCountString = Streams.readAsciiLine(in);
+ String blank = Streams.readAsciiLine(in);
+ if (!MAGIC.equals(magic)
+ || !VERSION_1.equals(version)
+ || !Integer.toString(appVersion).equals(appVersionString)
+ || !Integer.toString(valueCount).equals(valueCountString)
+ || !"".equals(blank)) {
+ throw new IOException("unexpected journal header: ["
+ + magic + ", " + version + ", " + valueCountString + ", " + blank + "]");
+ }
+
+ while (true) {
+ try {
+ readJournalLine(Streams.readAsciiLine(in));
+ } catch (EOFException endOfJournal) {
+ break;
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void readJournalLine(String line) throws IOException {
+ String[] parts = line.split(" ");
+ if (parts.length < 2) {
+ throw new IOException("unexpected journal line: " + line);
+ }
+
+ String key = parts[1];
+ if (parts[0].equals(REMOVE) && parts.length == 2) {
+ lruEntries.remove(key);
+ return;
+ }
+
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ }
+
+ if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
+ entry.readable = true;
+ entry.currentEditor = null;
+ entry.setLengths(Arrays.copyOfRange(parts, 2, parts.length));
+ } else if (parts[0].equals(DIRTY) && parts.length == 2) {
+ entry.currentEditor = new Editor(entry);
+ } else if (parts[0].equals(READ) && parts.length == 2) {
+ // this work was already done by calling lruEntries.get()
+ } else {
+ throw new IOException("unexpected journal line: " + line);
+ }
+ }
+
+ /**
+ * Computes the initial size and collects garbage as a part of opening the
+ * cache. Dirty entries are assumed to be inconsistent and will be deleted.
+ */
+ private void processJournal() throws IOException {
+ deleteIfExists(journalFileTmp);
+ for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) {
+ Entry entry = i.next();
+ if (entry.currentEditor == null) {
+ for (int t = 0; t < valueCount; t++) {
+ size += entry.lengths[t];
+ }
+ } else {
+ entry.currentEditor = null;
+ for (int t = 0; t < valueCount; t++) {
+ deleteIfExists(entry.getCleanFile(t));
+ deleteIfExists(entry.getDirtyFile(t));
+ }
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Creates a new journal that omits redundant information. This replaces the
+ * current journal if it exists.
+ */
+ private synchronized void rebuildJournal() throws IOException {
+ if (journalWriter != null) {
+ journalWriter.close();
+ }
+
+ Writer writer = new BufferedWriter(new FileWriter(journalFileTmp));
+ writer.write(MAGIC);
+ writer.write("\n");
+ writer.write(VERSION_1);
+ writer.write("\n");
+ writer.write(Integer.toString(appVersion));
+ writer.write("\n");
+ writer.write(Integer.toString(valueCount));
+ writer.write("\n");
+ writer.write("\n");
+
+ for (Entry entry : lruEntries.values()) {
+ if (entry.currentEditor != null) {
+ writer.write(DIRTY + ' ' + entry.key + '\n');
+ } else {
+ writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ }
+ }
+
+ writer.close();
+ journalFileTmp.renameTo(journalFile);
+ journalWriter = new BufferedWriter(new FileWriter(journalFile, true));
+ }
+
+ private static void deleteIfExists(File file) throws IOException {
+ Libcore.deleteIfExists(file);
+ }
+
+ /**
+ * Returns a snapshot of the entry named {@code key}, or null if it doesn't
+ * exist is not currently readable. If a value is returned, it is moved to
+ * the head of the LRU queue.
+ */
+ public synchronized Snapshot get(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ return null;
+ }
+
+ if (!entry.readable) {
+ return null;
+ }
+
+ /*
+ * Open all streams eagerly to guarantee that we see a single published
+ * snapshot. If we opened streams lazily then the streams could come
+ * from different edits.
+ */
+ InputStream[] ins = new InputStream[valueCount];
+ try {
+ for (int i = 0; i < valueCount; i++) {
+ ins[i] = new FileInputStream(entry.getCleanFile(i));
+ }
+ } catch (FileNotFoundException e) {
+ // a file must have been deleted manually!
+ return null;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(READ + ' ' + key + '\n');
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return new Snapshot(key, entry.sequenceNumber, ins);
+ }
+
+ /**
+ * Returns an editor for the entry named {@code key}, or null if another
+ * edit is in progress.
+ */
+ public Editor edit(String key) throws IOException {
+ return edit(key, ANY_SEQUENCE_NUMBER);
+ }
+
+ private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
+ && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
+ return null; // snapshot is stale
+ }
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ } else if (entry.currentEditor != null) {
+ return null; // another edit is in progress
+ }
+
+ Editor editor = new Editor(entry);
+ entry.currentEditor = editor;
+
+ // flush the journal before creating files to prevent file leaks
+ journalWriter.write(DIRTY + ' ' + key + '\n');
+ journalWriter.flush();
+ return editor;
+ }
+
+ /**
+ * Returns the directory where this cache stores its data.
+ */
+ public File getDirectory() {
+ return directory;
+ }
+
+ /**
+ * Returns the maximum number of bytes that this cache should use to store
+ * its data.
+ */
+ public long maxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Returns the number of bytes currently being used to store the values in
+ * this cache. This may be greater than the max size if a background
+ * deletion is pending.
+ */
+ public synchronized long size() {
+ return size;
+ }
+
+ private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
+ Entry entry = editor.entry;
+ if (entry.currentEditor != editor) {
+ throw new IllegalStateException();
+ }
+
+ // if this edit is creating the entry for the first time, every index must have a value
+ if (success && !entry.readable) {
+ for (int i = 0; i < valueCount; i++) {
+ if (!entry.getDirtyFile(i).exists()) {
+ editor.abort();
+ throw new IllegalStateException("edit didn't create file " + i);
+ }
+ }
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File dirty = entry.getDirtyFile(i);
+ if (success) {
+ if (dirty.exists()) {
+ File clean = entry.getCleanFile(i);
+ dirty.renameTo(clean);
+ long oldLength = entry.lengths[i];
+ long newLength = clean.length();
+ entry.lengths[i] = newLength;
+ size = size - oldLength + newLength;
+ }
+ } else {
+ deleteIfExists(dirty);
+ }
+ }
+
+ redundantOpCount++;
+ entry.currentEditor = null;
+ if (entry.readable | success) {
+ entry.readable = true;
+ journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ if (success) {
+ entry.sequenceNumber = nextSequenceNumber++;
+ }
+ } else {
+ lruEntries.remove(entry.key);
+ journalWriter.write(REMOVE + ' ' + entry.key + '\n');
+ }
+
+ if (size > maxSize || journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+ }
+
+ /**
+ * We only rebuild the journal when it will halve the size of the journal
+ * and eliminate at least 2000 ops.
+ */
+ private boolean journalRebuildRequired() {
+ final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;
+ return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD
+ && redundantOpCount >= lruEntries.size();
+ }
+
+ /**
+ * Drops the entry for {@code key} if it exists and can be removed. Entries
+ * actively being edited cannot be removed.
+ *
+ * @return true if an entry was removed.
+ */
+ public synchronized boolean remove(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null || entry.currentEditor != null) {
+ return false;
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File file = entry.getCleanFile(i);
+ if (!file.delete()) {
+ throw new IOException("failed to delete " + file);
+ }
+ size -= entry.lengths[i];
+ entry.lengths[i] = 0;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(REMOVE + ' ' + key + '\n');
+ lruEntries.remove(key);
+
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if this cache has been closed.
+ */
+ public boolean isClosed() {
+ return journalWriter == null;
+ }
+
+ private void checkNotClosed() {
+ if (journalWriter == null) {
+ throw new IllegalStateException("cache is closed");
+ }
+ }
+
+ /**
+ * Force buffered operations to the filesystem.
+ */
+ public synchronized void flush() throws IOException {
+ checkNotClosed();
+ trimToSize();
+ journalWriter.flush();
+ }
+
+ /**
+ * Closes this cache. Stored values will remain on the filesystem.
+ */
+ public synchronized void close() throws IOException {
+ if (journalWriter == null) {
+ return; // already closed
+ }
+ for (Entry entry : new ArrayList(lruEntries.values())) {
+ if (entry.currentEditor != null) {
+ entry.currentEditor.abort();
+ }
+ }
+ trimToSize();
+ journalWriter.close();
+ journalWriter = null;
+ }
+
+ private void trimToSize() throws IOException {
+ while (size > maxSize) {
+ Map.Entry toEvict = lruEntries.entrySet().iterator().next();
+ remove(toEvict.getKey());
+ }
+ }
+
+ /**
+ * Closes the cache and deletes all of its stored values. This will delete
+ * all files in the cache directory including files that weren't created by
+ * the cache.
+ */
+ public void delete() throws IOException {
+ close();
+ IoUtils.deleteContents(directory);
+ }
+
+ private void validateKey(String key) {
+ if (key.contains(" ") || key.contains("\n") || key.contains("\r")) {
+ throw new IllegalArgumentException(
+ "keys must not contain spaces or newlines: \"" + key + "\"");
+ }
+ }
+
+ private static String inputStreamToString(InputStream in) throws IOException {
+ return Streams.readFully(new InputStreamReader(in, Charsets.UTF_8));
+ }
+
+ /**
+ * A snapshot of the values for an entry.
+ */
+ public final class Snapshot implements Closeable {
+ private final String key;
+ private final long sequenceNumber;
+ private final InputStream[] ins;
+
+ private Snapshot(String key, long sequenceNumber, InputStream[] ins) {
+ this.key = key;
+ this.sequenceNumber = sequenceNumber;
+ this.ins = ins;
+ }
+
+ /**
+ * Returns an editor for this snapshot's entry, or null if either the
+ * entry has changed since this snapshot was created or if another edit
+ * is in progress.
+ */
+ public Editor edit() throws IOException {
+ return DiskLruCache.this.edit(key, sequenceNumber);
+ }
+
+ /**
+ * Returns the unbuffered stream with the value for {@code index}.
+ */
+ public InputStream getInputStream(int index) {
+ return ins[index];
+ }
+
+ /**
+ * Returns the string value for {@code index}.
+ */
+ public String getString(int index) throws IOException {
+ return inputStreamToString(getInputStream(index));
+ }
+
+ @Override public void close() {
+ for (InputStream in : ins) {
+ IoUtils.closeQuietly(in);
+ }
+ }
+ }
+
+ /**
+ * Edits the values for an entry.
+ */
+ public final class Editor {
+ private final Entry entry;
+ private boolean hasErrors;
+
+ private Editor(Entry entry) {
+ this.entry = entry;
+ }
+
+ /**
+ * Returns an unbuffered input stream to read the last committed value,
+ * or null if no value has been committed.
+ */
+ public InputStream newInputStream(int index) throws IOException {
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ if (!entry.readable) {
+ return null;
+ }
+ return new FileInputStream(entry.getCleanFile(index));
+ }
+ }
+
+ /**
+ * Returns the last committed value as a string, or null if no value
+ * has been committed.
+ */
+ public String getString(int index) throws IOException {
+ InputStream in = newInputStream(index);
+ return in != null ? inputStreamToString(in) : null;
+ }
+
+ /**
+ * Returns a new unbuffered output stream to write the value at
+ * {@code index}. If the underlying output stream encounters errors
+ * when writing to the filesystem, this edit will be aborted when
+ * {@link #commit} is called. The returned output stream does not throw
+ * IOExceptions.
+ */
+ public OutputStream newOutputStream(int index) throws IOException {
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
+ }
+ }
+
+ /**
+ * Sets the value at {@code index} to {@code value}.
+ */
+ public void set(int index, String value) throws IOException {
+ Writer writer = null;
+ try {
+ writer = new OutputStreamWriter(newOutputStream(index), Charsets.UTF_8);
+ writer.write(value);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ }
+
+ /**
+ * Commits this edit so it is visible to readers. This releases the
+ * edit lock so another edit may be started on the same key.
+ */
+ public void commit() throws IOException {
+ if (hasErrors) {
+ completeEdit(this, false);
+ remove(entry.key); // the previous entry is stale
+ } else {
+ completeEdit(this, true);
+ }
+ }
+
+ /**
+ * Aborts this edit. This releases the edit lock so another edit may be
+ * started on the same key.
+ */
+ public void abort() throws IOException {
+ completeEdit(this, false);
+ }
+
+ private class FaultHidingOutputStream extends FilterOutputStream {
+ private FaultHidingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override public void write(int oneByte) {
+ try {
+ out.write(oneByte);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void write(byte[] buffer, int offset, int length) {
+ try {
+ out.write(buffer, offset, length);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void close() {
+ try {
+ out.close();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void flush() {
+ try {
+ out.flush();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+ }
+ }
+
+ private final class Entry {
+ private final String key;
+
+ /** Lengths of this entry's files. */
+ private final long[] lengths;
+
+ /** True if this entry has ever been published */
+ private boolean readable;
+
+ /** The ongoing edit or null if this entry is not being edited. */
+ private Editor currentEditor;
+
+ /** The sequence number of the most recently committed edit to this entry. */
+ private long sequenceNumber;
+
+ private Entry(String key) {
+ this.key = key;
+ this.lengths = new long[valueCount];
+ }
+
+ public String getLengths() throws IOException {
+ StringBuilder result = new StringBuilder();
+ for (long size : lengths) {
+ result.append(' ').append(size);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Set lengths using decimal numbers like "10123".
+ */
+ private void setLengths(String[] strings) throws IOException {
+ if (strings.length != valueCount) {
+ throw invalidLengths(strings);
+ }
+
+ try {
+ for (int i = 0; i < strings.length; i++) {
+ lengths[i] = Long.parseLong(strings[i]);
+ }
+ } catch (NumberFormatException e) {
+ throw invalidLengths(strings);
+ }
+ }
+
+ private IOException invalidLengths(String[] strings) throws IOException {
+ throw new IOException("unexpected journal line: " + Arrays.toString(strings));
+ }
+
+ public File getCleanFile(int i) {
+ return new File(directory, key + "." + i);
+ }
+
+ public File getDirtyFile(int i) {
+ return new File(directory, key + "." + i + ".tmp");
+ }
+ }
+}
diff --git a/src/main/java/libcore/io/IoUtils.java b/src/main/java/libcore/io/IoUtils.java
new file mode 100644
index 000000000000..307737d5b5a6
--- /dev/null
+++ b/src/main/java/libcore/io/IoUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 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 libcore.io;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+
+public final class IoUtils {
+ private IoUtils() {
+ }
+
+ /**
+ * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
+ */
+ public static void closeQuietly(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
+ * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null.
+ */
+ public static void closeQuietly(Socket socket) {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
+ * Recursively delete everything in {@code dir}.
+ */
+ // TODO: this should specify paths as Strings rather than as Files
+ public static void deleteContents(File dir) throws IOException {
+ File[] files = dir.listFiles();
+ if (files == null) {
+ throw new IllegalArgumentException("not a directory: " + dir);
+ }
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ if (!file.delete()) {
+ throw new IOException("failed to delete file: " + file);
+ }
+ }
+ }
+}
diff --git a/src/main/java/libcore/io/OsConstants.java b/src/main/java/libcore/io/OsConstants.java
new file mode 100644
index 000000000000..68a165c8de75
--- /dev/null
+++ b/src/main/java/libcore/io/OsConstants.java
@@ -0,0 +1,724 @@
+/*
+ * 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 libcore.io;
+
+public final class OsConstants {
+ private OsConstants() { }
+
+ public static boolean S_ISBLK(int mode) { return (mode & S_IFMT) == S_IFBLK; }
+ public static boolean S_ISCHR(int mode) { return (mode & S_IFMT) == S_IFCHR; }
+ public static boolean S_ISDIR(int mode) { return (mode & S_IFMT) == S_IFDIR; }
+ public static boolean S_ISFIFO(int mode) { return (mode & S_IFMT) == S_IFIFO; }
+ public static boolean S_ISREG(int mode) { return (mode & S_IFMT) == S_IFREG; }
+ public static boolean S_ISLNK(int mode) { return (mode & S_IFMT) == S_IFLNK; }
+ public static boolean S_ISSOCK(int mode) { return (mode & S_IFMT) == S_IFSOCK; }
+
+ public static int WEXITSTATUS(int status) { return (status & 0xff00) >> 8; }
+ public static boolean WCOREDUMP(int status) { return (status & 0x80) != 0; }
+ public static int WTERMSIG(int status) { return status & 0x7f; }
+ public static int WSTOPSIG(int status) { return WEXITSTATUS(status); }
+ public static boolean WIFEXITED(int status) { return (WTERMSIG(status) == 0); }
+ public static boolean WIFSTOPPED(int status) { return (WTERMSIG(status) == 0x7f); }
+ public static boolean WIFSIGNALED(int status) { return (WTERMSIG(status + 1) >= 2); }
+
+ public static final int AF_INET = placeholder();
+ public static final int AF_INET6 = placeholder();
+ public static final int AF_UNIX = placeholder();
+ public static final int AF_UNSPEC = placeholder();
+ public static final int AI_ADDRCONFIG = placeholder();
+ public static final int AI_ALL = placeholder();
+ public static final int AI_CANONNAME = placeholder();
+ public static final int AI_NUMERICHOST = placeholder();
+ public static final int AI_NUMERICSERV = placeholder();
+ public static final int AI_PASSIVE = placeholder();
+ public static final int AI_V4MAPPED = placeholder();
+ public static final int E2BIG = placeholder();
+ public static final int EACCES = placeholder();
+ public static final int EADDRINUSE = placeholder();
+ public static final int EADDRNOTAVAIL = placeholder();
+ public static final int EAFNOSUPPORT = placeholder();
+ public static final int EAGAIN = placeholder();
+ public static final int EAI_AGAIN = placeholder();
+ public static final int EAI_BADFLAGS = placeholder();
+ public static final int EAI_FAIL = placeholder();
+ public static final int EAI_FAMILY = placeholder();
+ public static final int EAI_MEMORY = placeholder();
+ public static final int EAI_NODATA = placeholder();
+ public static final int EAI_NONAME = placeholder();
+ public static final int EAI_OVERFLOW = placeholder();
+ public static final int EAI_SERVICE = placeholder();
+ public static final int EAI_SOCKTYPE = placeholder();
+ public static final int EAI_SYSTEM = placeholder();
+ public static final int EALREADY = placeholder();
+ public static final int EBADF = placeholder();
+ public static final int EBADMSG = placeholder();
+ public static final int EBUSY = placeholder();
+ public static final int ECANCELED = placeholder();
+ public static final int ECHILD = placeholder();
+ public static final int ECONNABORTED = placeholder();
+ public static final int ECONNREFUSED = placeholder();
+ public static final int ECONNRESET = placeholder();
+ public static final int EDEADLK = placeholder();
+ public static final int EDESTADDRREQ = placeholder();
+ public static final int EDOM = placeholder();
+ public static final int EDQUOT = placeholder();
+ public static final int EEXIST = placeholder();
+ public static final int EFAULT = placeholder();
+ public static final int EFBIG = placeholder();
+ public static final int EHOSTUNREACH = placeholder();
+ public static final int EIDRM = placeholder();
+ public static final int EILSEQ = placeholder();
+ public static final int EINPROGRESS = placeholder();
+ public static final int EINTR = placeholder();
+ public static final int EINVAL = placeholder();
+ public static final int EIO = placeholder();
+ public static final int EISCONN = placeholder();
+ public static final int EISDIR = placeholder();
+ public static final int ELOOP = placeholder();
+ public static final int EMFILE = placeholder();
+ public static final int EMLINK = placeholder();
+ public static final int EMSGSIZE = placeholder();
+ public static final int EMULTIHOP = placeholder();
+ public static final int ENAMETOOLONG = placeholder();
+ public static final int ENETDOWN = placeholder();
+ public static final int ENETRESET = placeholder();
+ public static final int ENETUNREACH = placeholder();
+ public static final int ENFILE = placeholder();
+ public static final int ENOBUFS = placeholder();
+ public static final int ENODATA = placeholder();
+ public static final int ENODEV = placeholder();
+ public static final int ENOENT = placeholder();
+ public static final int ENOEXEC = placeholder();
+ public static final int ENOLCK = placeholder();
+ public static final int ENOLINK = placeholder();
+ public static final int ENOMEM = placeholder();
+ public static final int ENOMSG = placeholder();
+ public static final int ENOPROTOOPT = placeholder();
+ public static final int ENOSPC = placeholder();
+ public static final int ENOSR = placeholder();
+ public static final int ENOSTR = placeholder();
+ public static final int ENOSYS = placeholder();
+ public static final int ENOTCONN = placeholder();
+ public static final int ENOTDIR = placeholder();
+ public static final int ENOTEMPTY = placeholder();
+ public static final int ENOTSOCK = placeholder();
+ public static final int ENOTSUP = placeholder();
+ public static final int ENOTTY = placeholder();
+ public static final int ENXIO = placeholder();
+ public static final int EOPNOTSUPP = placeholder();
+ public static final int EOVERFLOW = placeholder();
+ public static final int EPERM = placeholder();
+ public static final int EPIPE = placeholder();
+ public static final int EPROTO = placeholder();
+ public static final int EPROTONOSUPPORT = placeholder();
+ public static final int EPROTOTYPE = placeholder();
+ public static final int ERANGE = placeholder();
+ public static final int EROFS = placeholder();
+ public static final int ESPIPE = placeholder();
+ public static final int ESRCH = placeholder();
+ public static final int ESTALE = placeholder();
+ public static final int ETIME = placeholder();
+ public static final int ETIMEDOUT = placeholder();
+ public static final int ETXTBSY = placeholder();
+ public static final int EWOULDBLOCK = placeholder();
+ public static final int EXDEV = placeholder();
+ public static final int EXIT_FAILURE = placeholder();
+ public static final int EXIT_SUCCESS = placeholder();
+ public static final int FD_CLOEXEC = placeholder();
+ public static final int FIONREAD = placeholder();
+ public static final int F_DUPFD = placeholder();
+ public static final int F_GETFD = placeholder();
+ public static final int F_GETFL = placeholder();
+ public static final int F_GETLK = placeholder();
+ public static final int F_GETLK64 = placeholder();
+ public static final int F_GETOWN = placeholder();
+ public static final int F_OK = placeholder();
+ public static final int F_RDLCK = placeholder();
+ public static final int F_SETFD = placeholder();
+ public static final int F_SETFL = placeholder();
+ public static final int F_SETLK = placeholder();
+ public static final int F_SETLK64 = placeholder();
+ public static final int F_SETLKW = placeholder();
+ public static final int F_SETLKW64 = placeholder();
+ public static final int F_SETOWN = placeholder();
+ public static final int F_UNLCK = placeholder();
+ public static final int F_WRLCK = placeholder();
+ public static final int IFF_ALLMULTI = placeholder();
+ public static final int IFF_AUTOMEDIA = placeholder();
+ public static final int IFF_BROADCAST = placeholder();
+ public static final int IFF_DEBUG = placeholder();
+ public static final int IFF_DYNAMIC = placeholder();
+ public static final int IFF_LOOPBACK = placeholder();
+ public static final int IFF_MASTER = placeholder();
+ public static final int IFF_MULTICAST = placeholder();
+ public static final int IFF_NOARP = placeholder();
+ public static final int IFF_NOTRAILERS = placeholder();
+ public static final int IFF_POINTOPOINT = placeholder();
+ public static final int IFF_PORTSEL = placeholder();
+ public static final int IFF_PROMISC = placeholder();
+ public static final int IFF_RUNNING = placeholder();
+ public static final int IFF_SLAVE = placeholder();
+ public static final int IFF_UP = placeholder();
+ public static final int IPPROTO_ICMP = placeholder();
+ public static final int IPPROTO_IP = placeholder();
+ public static final int IPPROTO_IPV6 = placeholder();
+ public static final int IPPROTO_RAW = placeholder();
+ public static final int IPPROTO_TCP = placeholder();
+ public static final int IPPROTO_UDP = placeholder();
+ public static final int IPV6_CHECKSUM = placeholder();
+ public static final int IPV6_MULTICAST_HOPS = placeholder();
+ public static final int IPV6_MULTICAST_IF = placeholder();
+ public static final int IPV6_MULTICAST_LOOP = placeholder();
+ public static final int IPV6_RECVDSTOPTS = placeholder();
+ public static final int IPV6_RECVHOPLIMIT = placeholder();
+ public static final int IPV6_RECVHOPOPTS = placeholder();
+ public static final int IPV6_RECVPKTINFO = placeholder();
+ public static final int IPV6_RECVRTHDR = placeholder();
+ public static final int IPV6_RECVTCLASS = placeholder();
+ public static final int IPV6_TCLASS = placeholder();
+ public static final int IPV6_UNICAST_HOPS = placeholder();
+ public static final int IPV6_V6ONLY = placeholder();
+ public static final int IP_MULTICAST_IF = placeholder();
+ public static final int IP_MULTICAST_LOOP = placeholder();
+ public static final int IP_MULTICAST_TTL = placeholder();
+ public static final int IP_TOS = placeholder();
+ public static final int IP_TTL = placeholder();
+ public static final int MAP_FIXED = placeholder();
+ public static final int MAP_PRIVATE = placeholder();
+ public static final int MAP_SHARED = placeholder();
+ public static final int MCAST_JOIN_GROUP = placeholder();
+ public static final int MCAST_LEAVE_GROUP = placeholder();
+ public static final int MCL_CURRENT = placeholder();
+ public static final int MCL_FUTURE = placeholder();
+ public static final int MSG_CTRUNC = placeholder();
+ public static final int MSG_DONTROUTE = placeholder();
+ public static final int MSG_EOR = placeholder();
+ public static final int MSG_OOB = placeholder();
+ public static final int MSG_PEEK = placeholder();
+ public static final int MSG_TRUNC = placeholder();
+ public static final int MSG_WAITALL = placeholder();
+ public static final int MS_ASYNC = placeholder();
+ public static final int MS_INVALIDATE = placeholder();
+ public static final int MS_SYNC = placeholder();
+ public static final int NI_DGRAM = placeholder();
+ public static final int NI_NAMEREQD = placeholder();
+ public static final int NI_NOFQDN = placeholder();
+ public static final int NI_NUMERICHOST = placeholder();
+ public static final int NI_NUMERICSERV = placeholder();
+ public static final int O_ACCMODE = placeholder();
+ public static final int O_APPEND = placeholder();
+ public static final int O_CREAT = placeholder();
+ public static final int O_EXCL = placeholder();
+ public static final int O_NOCTTY = placeholder();
+ public static final int O_NONBLOCK = placeholder();
+ public static final int O_RDONLY = placeholder();
+ public static final int O_RDWR = placeholder();
+ public static final int O_SYNC = placeholder();
+ public static final int O_TRUNC = placeholder();
+ public static final int O_WRONLY = placeholder();
+ public static final int POLLERR = placeholder();
+ public static final int POLLHUP = placeholder();
+ public static final int POLLIN = placeholder();
+ public static final int POLLNVAL = placeholder();
+ public static final int POLLOUT = placeholder();
+ public static final int POLLPRI = placeholder();
+ public static final int POLLRDBAND = placeholder();
+ public static final int POLLRDNORM = placeholder();
+ public static final int POLLWRBAND = placeholder();
+ public static final int POLLWRNORM = placeholder();
+ public static final int PROT_EXEC = placeholder();
+ public static final int PROT_NONE = placeholder();
+ public static final int PROT_READ = placeholder();
+ public static final int PROT_WRITE = placeholder();
+ public static final int R_OK = placeholder();
+ public static final int SEEK_CUR = placeholder();
+ public static final int SEEK_END = placeholder();
+ public static final int SEEK_SET = placeholder();
+ public static final int SHUT_RD = placeholder();
+ public static final int SHUT_RDWR = placeholder();
+ public static final int SHUT_WR = placeholder();
+ public static final int SIGABRT = placeholder();
+ public static final int SIGALRM = placeholder();
+ public static final int SIGBUS = placeholder();
+ public static final int SIGCHLD = placeholder();
+ public static final int SIGCONT = placeholder();
+ public static final int SIGFPE = placeholder();
+ public static final int SIGHUP = placeholder();
+ public static final int SIGILL = placeholder();
+ public static final int SIGINT = placeholder();
+ public static final int SIGIO = placeholder();
+ public static final int SIGKILL = placeholder();
+ public static final int SIGPIPE = placeholder();
+ public static final int SIGPROF = placeholder();
+ public static final int SIGPWR = placeholder();
+ public static final int SIGQUIT = placeholder();
+ public static final int SIGRTMAX = placeholder();
+ public static final int SIGRTMIN = placeholder();
+ public static final int SIGSEGV = placeholder();
+ public static final int SIGSTKFLT = placeholder();
+ public static final int SIGSTOP = placeholder();
+ public static final int SIGSYS = placeholder();
+ public static final int SIGTERM = placeholder();
+ public static final int SIGTRAP = placeholder();
+ public static final int SIGTSTP = placeholder();
+ public static final int SIGTTIN = placeholder();
+ public static final int SIGTTOU = placeholder();
+ public static final int SIGURG = placeholder();
+ public static final int SIGUSR1 = placeholder();
+ public static final int SIGUSR2 = placeholder();
+ public static final int SIGVTALRM = placeholder();
+ public static final int SIGWINCH = placeholder();
+ public static final int SIGXCPU = placeholder();
+ public static final int SIGXFSZ = placeholder();
+ public static final int SIOCGIFADDR = placeholder();
+ public static final int SIOCGIFBRDADDR = placeholder();
+ public static final int SIOCGIFDSTADDR = placeholder();
+ public static final int SIOCGIFNETMASK = placeholder();
+ public static final int SOCK_DGRAM = placeholder();
+ public static final int SOCK_RAW = placeholder();
+ public static final int SOCK_SEQPACKET = placeholder();
+ public static final int SOCK_STREAM = placeholder();
+ public static final int SOL_SOCKET = placeholder();
+ public static final int SO_BINDTODEVICE = placeholder();
+ public static final int SO_BROADCAST = placeholder();
+ public static final int SO_DEBUG = placeholder();
+ public static final int SO_DONTROUTE = placeholder();
+ public static final int SO_ERROR = placeholder();
+ public static final int SO_KEEPALIVE = placeholder();
+ public static final int SO_LINGER = placeholder();
+ public static final int SO_OOBINLINE = placeholder();
+ public static final int SO_RCVBUF = placeholder();
+ public static final int SO_RCVLOWAT = placeholder();
+ public static final int SO_RCVTIMEO = placeholder();
+ public static final int SO_REUSEADDR = placeholder();
+ public static final int SO_SNDBUF = placeholder();
+ public static final int SO_SNDLOWAT = placeholder();
+ public static final int SO_SNDTIMEO = placeholder();
+ public static final int SO_TYPE = placeholder();
+ public static final int STDERR_FILENO = placeholder();
+ public static final int STDIN_FILENO = placeholder();
+ public static final int STDOUT_FILENO = placeholder();
+ public static final int S_IFBLK = placeholder();
+ public static final int S_IFCHR = placeholder();
+ public static final int S_IFDIR = placeholder();
+ public static final int S_IFIFO = placeholder();
+ public static final int S_IFLNK = placeholder();
+ public static final int S_IFMT = placeholder();
+ public static final int S_IFREG = placeholder();
+ public static final int S_IFSOCK = placeholder();
+ public static final int S_IRGRP = placeholder();
+ public static final int S_IROTH = placeholder();
+ public static final int S_IRUSR = placeholder();
+ public static final int S_IRWXG = placeholder();
+ public static final int S_IRWXO = placeholder();
+ public static final int S_IRWXU = placeholder();
+ public static final int S_ISGID = placeholder();
+ public static final int S_ISUID = placeholder();
+ public static final int S_ISVTX = placeholder();
+ public static final int S_IWGRP = placeholder();
+ public static final int S_IWOTH = placeholder();
+ public static final int S_IWUSR = placeholder();
+ public static final int S_IXGRP = placeholder();
+ public static final int S_IXOTH = placeholder();
+ public static final int S_IXUSR = placeholder();
+ public static final int TCP_NODELAY = placeholder();
+ public static final int WCONTINUED = placeholder();
+ public static final int WEXITED = placeholder();
+ public static final int WNOHANG = placeholder();
+ public static final int WNOWAIT = placeholder();
+ public static final int WSTOPPED = placeholder();
+ public static final int WUNTRACED = placeholder();
+ public static final int W_OK = placeholder();
+ public static final int X_OK = placeholder();
+ public static final int _SC_2_CHAR_TERM = placeholder();
+ public static final int _SC_2_C_BIND = placeholder();
+ public static final int _SC_2_C_DEV = placeholder();
+ public static final int _SC_2_C_VERSION = placeholder();
+ public static final int _SC_2_FORT_DEV = placeholder();
+ public static final int _SC_2_FORT_RUN = placeholder();
+ public static final int _SC_2_LOCALEDEF = placeholder();
+ public static final int _SC_2_SW_DEV = placeholder();
+ public static final int _SC_2_UPE = placeholder();
+ public static final int _SC_2_VERSION = placeholder();
+ public static final int _SC_AIO_LISTIO_MAX = placeholder();
+ public static final int _SC_AIO_MAX = placeholder();
+ public static final int _SC_AIO_PRIO_DELTA_MAX = placeholder();
+ public static final int _SC_ARG_MAX = placeholder();
+ public static final int _SC_ASYNCHRONOUS_IO = placeholder();
+ public static final int _SC_ATEXIT_MAX = placeholder();
+ public static final int _SC_AVPHYS_PAGES = placeholder();
+ public static final int _SC_BC_BASE_MAX = placeholder();
+ public static final int _SC_BC_DIM_MAX = placeholder();
+ public static final int _SC_BC_SCALE_MAX = placeholder();
+ public static final int _SC_BC_STRING_MAX = placeholder();
+ public static final int _SC_CHILD_MAX = placeholder();
+ public static final int _SC_CLK_TCK = placeholder();
+ public static final int _SC_COLL_WEIGHTS_MAX = placeholder();
+ public static final int _SC_DELAYTIMER_MAX = placeholder();
+ public static final int _SC_EXPR_NEST_MAX = placeholder();
+ public static final int _SC_FSYNC = placeholder();
+ public static final int _SC_GETGR_R_SIZE_MAX = placeholder();
+ public static final int _SC_GETPW_R_SIZE_MAX = placeholder();
+ public static final int _SC_IOV_MAX = placeholder();
+ public static final int _SC_JOB_CONTROL = placeholder();
+ public static final int _SC_LINE_MAX = placeholder();
+ public static final int _SC_LOGIN_NAME_MAX = placeholder();
+ public static final int _SC_MAPPED_FILES = placeholder();
+ public static final int _SC_MEMLOCK = placeholder();
+ public static final int _SC_MEMLOCK_RANGE = placeholder();
+ public static final int _SC_MEMORY_PROTECTION = placeholder();
+ public static final int _SC_MESSAGE_PASSING = placeholder();
+ public static final int _SC_MQ_OPEN_MAX = placeholder();
+ public static final int _SC_MQ_PRIO_MAX = placeholder();
+ public static final int _SC_NGROUPS_MAX = placeholder();
+ public static final int _SC_NPROCESSORS_CONF = placeholder();
+ public static final int _SC_NPROCESSORS_ONLN = placeholder();
+ public static final int _SC_OPEN_MAX = placeholder();
+ public static final int _SC_PAGESIZE = placeholder();
+ public static final int _SC_PAGE_SIZE = placeholder();
+ public static final int _SC_PASS_MAX = placeholder();
+ public static final int _SC_PHYS_PAGES = placeholder();
+ public static final int _SC_PRIORITIZED_IO = placeholder();
+ public static final int _SC_PRIORITY_SCHEDULING = placeholder();
+ public static final int _SC_REALTIME_SIGNALS = placeholder();
+ public static final int _SC_RE_DUP_MAX = placeholder();
+ public static final int _SC_RTSIG_MAX = placeholder();
+ public static final int _SC_SAVED_IDS = placeholder();
+ public static final int _SC_SEMAPHORES = placeholder();
+ public static final int _SC_SEM_NSEMS_MAX = placeholder();
+ public static final int _SC_SEM_VALUE_MAX = placeholder();
+ public static final int _SC_SHARED_MEMORY_OBJECTS = placeholder();
+ public static final int _SC_SIGQUEUE_MAX = placeholder();
+ public static final int _SC_STREAM_MAX = placeholder();
+ public static final int _SC_SYNCHRONIZED_IO = placeholder();
+ public static final int _SC_THREADS = placeholder();
+ public static final int _SC_THREAD_ATTR_STACKADDR = placeholder();
+ public static final int _SC_THREAD_ATTR_STACKSIZE = placeholder();
+ public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS = placeholder();
+ public static final int _SC_THREAD_KEYS_MAX = placeholder();
+ public static final int _SC_THREAD_PRIORITY_SCHEDULING = placeholder();
+ public static final int _SC_THREAD_PRIO_INHERIT = placeholder();
+ public static final int _SC_THREAD_PRIO_PROTECT = placeholder();
+ public static final int _SC_THREAD_SAFE_FUNCTIONS = placeholder();
+ public static final int _SC_THREAD_STACK_MIN = placeholder();
+ public static final int _SC_THREAD_THREADS_MAX = placeholder();
+ public static final int _SC_TIMERS = placeholder();
+ public static final int _SC_TIMER_MAX = placeholder();
+ public static final int _SC_TTY_NAME_MAX = placeholder();
+ public static final int _SC_TZNAME_MAX = placeholder();
+ public static final int _SC_VERSION = placeholder();
+ public static final int _SC_XBS5_ILP32_OFF32 = placeholder();
+ public static final int _SC_XBS5_ILP32_OFFBIG = placeholder();
+ public static final int _SC_XBS5_LP64_OFF64 = placeholder();
+ public static final int _SC_XBS5_LPBIG_OFFBIG = placeholder();
+ public static final int _SC_XOPEN_CRYPT = placeholder();
+ public static final int _SC_XOPEN_ENH_I18N = placeholder();
+ public static final int _SC_XOPEN_LEGACY = placeholder();
+ public static final int _SC_XOPEN_REALTIME = placeholder();
+ public static final int _SC_XOPEN_REALTIME_THREADS = placeholder();
+ public static final int _SC_XOPEN_SHM = placeholder();
+ public static final int _SC_XOPEN_UNIX = placeholder();
+ public static final int _SC_XOPEN_VERSION = placeholder();
+ public static final int _SC_XOPEN_XCU_VERSION = placeholder();
+
+ public static String gaiName(int error) {
+ if (error == EAI_AGAIN) {
+ return "EAI_AGAIN";
+ }
+ if (error == EAI_BADFLAGS) {
+ return "EAI_BADFLAGS";
+ }
+ if (error == EAI_FAIL) {
+ return "EAI_FAIL";
+ }
+ if (error == EAI_FAMILY) {
+ return "EAI_FAMILY";
+ }
+ if (error == EAI_MEMORY) {
+ return "EAI_MEMORY";
+ }
+ if (error == EAI_NODATA) {
+ return "EAI_NODATA";
+ }
+ if (error == EAI_NONAME) {
+ return "EAI_NONAME";
+ }
+ if (error == EAI_OVERFLOW) {
+ return "EAI_OVERFLOW";
+ }
+ if (error == EAI_SERVICE) {
+ return "EAI_SERVICE";
+ }
+ if (error == EAI_SOCKTYPE) {
+ return "EAI_SOCKTYPE";
+ }
+ if (error == EAI_SYSTEM) {
+ return "EAI_SYSTEM";
+ }
+ return null;
+ }
+
+ public static String errnoName(int errno) {
+ if (errno == E2BIG) {
+ return "E2BIG";
+ }
+ if (errno == EACCES) {
+ return "EACCES";
+ }
+ if (errno == EADDRINUSE) {
+ return "EADDRINUSE";
+ }
+ if (errno == EADDRNOTAVAIL) {
+ return "EADDRNOTAVAIL";
+ }
+ if (errno == EAFNOSUPPORT) {
+ return "EAFNOSUPPORT";
+ }
+ if (errno == EAGAIN) {
+ return "EAGAIN";
+ }
+ if (errno == EALREADY) {
+ return "EALREADY";
+ }
+ if (errno == EBADF) {
+ return "EBADF";
+ }
+ if (errno == EBADMSG) {
+ return "EBADMSG";
+ }
+ if (errno == EBUSY) {
+ return "EBUSY";
+ }
+ if (errno == ECANCELED) {
+ return "ECANCELED";
+ }
+ if (errno == ECHILD) {
+ return "ECHILD";
+ }
+ if (errno == ECONNABORTED) {
+ return "ECONNABORTED";
+ }
+ if (errno == ECONNREFUSED) {
+ return "ECONNREFUSED";
+ }
+ if (errno == ECONNRESET) {
+ return "ECONNRESET";
+ }
+ if (errno == EDEADLK) {
+ return "EDEADLK";
+ }
+ if (errno == EDESTADDRREQ) {
+ return "EDESTADDRREQ";
+ }
+ if (errno == EDOM) {
+ return "EDOM";
+ }
+ if (errno == EDQUOT) {
+ return "EDQUOT";
+ }
+ if (errno == EEXIST) {
+ return "EEXIST";
+ }
+ if (errno == EFAULT) {
+ return "EFAULT";
+ }
+ if (errno == EFBIG) {
+ return "EFBIG";
+ }
+ if (errno == EHOSTUNREACH) {
+ return "EHOSTUNREACH";
+ }
+ if (errno == EIDRM) {
+ return "EIDRM";
+ }
+ if (errno == EILSEQ) {
+ return "EILSEQ";
+ }
+ if (errno == EINPROGRESS) {
+ return "EINPROGRESS";
+ }
+ if (errno == EINTR) {
+ return "EINTR";
+ }
+ if (errno == EINVAL) {
+ return "EINVAL";
+ }
+ if (errno == EIO) {
+ return "EIO";
+ }
+ if (errno == EISCONN) {
+ return "EISCONN";
+ }
+ if (errno == EISDIR) {
+ return "EISDIR";
+ }
+ if (errno == ELOOP) {
+ return "ELOOP";
+ }
+ if (errno == EMFILE) {
+ return "EMFILE";
+ }
+ if (errno == EMLINK) {
+ return "EMLINK";
+ }
+ if (errno == EMSGSIZE) {
+ return "EMSGSIZE";
+ }
+ if (errno == EMULTIHOP) {
+ return "EMULTIHOP";
+ }
+ if (errno == ENAMETOOLONG) {
+ return "ENAMETOOLONG";
+ }
+ if (errno == ENETDOWN) {
+ return "ENETDOWN";
+ }
+ if (errno == ENETRESET) {
+ return "ENETRESET";
+ }
+ if (errno == ENETUNREACH) {
+ return "ENETUNREACH";
+ }
+ if (errno == ENFILE) {
+ return "ENFILE";
+ }
+ if (errno == ENOBUFS) {
+ return "ENOBUFS";
+ }
+ if (errno == ENODATA) {
+ return "ENODATA";
+ }
+ if (errno == ENODEV) {
+ return "ENODEV";
+ }
+ if (errno == ENOENT) {
+ return "ENOENT";
+ }
+ if (errno == ENOEXEC) {
+ return "ENOEXEC";
+ }
+ if (errno == ENOLCK) {
+ return "ENOLCK";
+ }
+ if (errno == ENOLINK) {
+ return "ENOLINK";
+ }
+ if (errno == ENOMEM) {
+ return "ENOMEM";
+ }
+ if (errno == ENOMSG) {
+ return "ENOMSG";
+ }
+ if (errno == ENOPROTOOPT) {
+ return "ENOPROTOOPT";
+ }
+ if (errno == ENOSPC) {
+ return "ENOSPC";
+ }
+ if (errno == ENOSR) {
+ return "ENOSR";
+ }
+ if (errno == ENOSTR) {
+ return "ENOSTR";
+ }
+ if (errno == ENOSYS) {
+ return "ENOSYS";
+ }
+ if (errno == ENOTCONN) {
+ return "ENOTCONN";
+ }
+ if (errno == ENOTDIR) {
+ return "ENOTDIR";
+ }
+ if (errno == ENOTEMPTY) {
+ return "ENOTEMPTY";
+ }
+ if (errno == ENOTSOCK) {
+ return "ENOTSOCK";
+ }
+ if (errno == ENOTSUP) {
+ return "ENOTSUP";
+ }
+ if (errno == ENOTTY) {
+ return "ENOTTY";
+ }
+ if (errno == ENXIO) {
+ return "ENXIO";
+ }
+ if (errno == EOPNOTSUPP) {
+ return "EOPNOTSUPP";
+ }
+ if (errno == EOVERFLOW) {
+ return "EOVERFLOW";
+ }
+ if (errno == EPERM) {
+ return "EPERM";
+ }
+ if (errno == EPIPE) {
+ return "EPIPE";
+ }
+ if (errno == EPROTO) {
+ return "EPROTO";
+ }
+ if (errno == EPROTONOSUPPORT) {
+ return "EPROTONOSUPPORT";
+ }
+ if (errno == EPROTOTYPE) {
+ return "EPROTOTYPE";
+ }
+ if (errno == ERANGE) {
+ return "ERANGE";
+ }
+ if (errno == EROFS) {
+ return "EROFS";
+ }
+ if (errno == ESPIPE) {
+ return "ESPIPE";
+ }
+ if (errno == ESRCH) {
+ return "ESRCH";
+ }
+ if (errno == ESTALE) {
+ return "ESTALE";
+ }
+ if (errno == ETIME) {
+ return "ETIME";
+ }
+ if (errno == ETIMEDOUT) {
+ return "ETIMEDOUT";
+ }
+ if (errno == ETXTBSY) {
+ return "ETXTBSY";
+ }
+ if (errno == EWOULDBLOCK) {
+ return "EWOULDBLOCK";
+ }
+ if (errno == EXDEV) {
+ return "EXDEV";
+ }
+ return null;
+ }
+
+ private static native void initConstants();
+
+ // A hack to avoid these constants being inlined by javac...
+ private static int placeholder() { return 0; }
+ // ...because we want to initialize them at runtime.
+ static {
+ initConstants();
+ }
+}
diff --git a/src/main/java/libcore/io/SizeOf.java b/src/main/java/libcore/io/SizeOf.java
new file mode 100644
index 000000000000..728fbfce7a0c
--- /dev/null
+++ b/src/main/java/libcore/io/SizeOf.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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 libcore.io;
+
+public final class SizeOf {
+ public static final int CHAR = 2;
+ public static final int DOUBLE = 8;
+ public static final int FLOAT = 4;
+ public static final int INT = 4;
+ public static final int LONG = 8;
+ public static final int SHORT = 2;
+
+ private SizeOf() {
+ }
+}
diff --git a/src/main/java/libcore/io/Streams.java b/src/main/java/libcore/io/Streams.java
new file mode 100644
index 000000000000..194b77510371
--- /dev/null
+++ b/src/main/java/libcore/io/Streams.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 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 libcore.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.concurrent.atomic.AtomicReference;
+import libcore.util.Libcore;
+
+public final class Streams {
+ private static AtomicReference skipBuffer = new AtomicReference();
+
+ private Streams() {}
+
+ /**
+ * Implements InputStream.read(int) in terms of InputStream.read(byte[], int, int).
+ * InputStream assumes that you implement InputStream.read(int) and provides default
+ * implementations of the others, but often the opposite is more efficient.
+ */
+ public static int readSingleByte(InputStream in) throws IOException {
+ byte[] buffer = new byte[1];
+ int result = in.read(buffer, 0, 1);
+ return (result != -1) ? buffer[0] & 0xff : -1;
+ }
+
+ /**
+ * Implements OutputStream.write(int) in terms of OutputStream.write(byte[], int, int).
+ * OutputStream assumes that you implement OutputStream.write(int) and provides default
+ * implementations of the others, but often the opposite is more efficient.
+ */
+ public static void writeSingleByte(OutputStream out, int b) throws IOException {
+ byte[] buffer = new byte[1];
+ buffer[0] = (byte) (b & 0xff);
+ out.write(buffer);
+ }
+
+ /**
+ * Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available.
+ */
+ public static void readFully(InputStream in, byte[] dst) throws IOException {
+ readFully(in, dst, 0, dst.length);
+ }
+
+ /**
+ * Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws
+ * EOFException if insufficient bytes are available.
+ *
+ * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
+ */
+ public static void readFully(InputStream in, byte[] dst, int offset, int byteCount) throws IOException {
+ if (byteCount == 0) {
+ return;
+ }
+ if (in == null) {
+ throw new NullPointerException("in == null");
+ }
+ if (dst == null) {
+ throw new NullPointerException("dst == null");
+ }
+ Libcore.checkOffsetAndCount(dst.length, offset, byteCount);
+ while (byteCount > 0) {
+ int bytesRead = in.read(dst, offset, byteCount);
+ if (bytesRead < 0) {
+ throw new EOFException();
+ }
+ offset += bytesRead;
+ byteCount -= bytesRead;
+ }
+ }
+
+ /**
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
+ */
+ public static byte[] readFully(InputStream in) throws IOException {
+ try {
+ return readFullyNoClose(in);
+ } finally {
+ in.close();
+ }
+ }
+
+ /**
+ * Returns a byte[] containing the remainder of 'in'.
+ */
+ public static byte[] readFullyNoClose(InputStream in) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+ return bytes.toByteArray();
+ }
+
+ /**
+ * Returns the remainder of 'reader' as a string, closing it when done.
+ */
+ public static String readFully(Reader reader) throws IOException {
+ try {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ return writer.toString();
+ } finally {
+ reader.close();
+ }
+ }
+
+ public static void skipAll(InputStream in) throws IOException {
+ do {
+ in.skip(Long.MAX_VALUE);
+ } while (in.read() != -1);
+ }
+
+ /**
+ * Call {@code in.read()} repeatedly until either the stream is exhausted or
+ * {@code byteCount} bytes have been read.
+ *
+ *
This method reuses the skip buffer but is careful to never use it at
+ * the same time that another stream is using it. Otherwise streams that use
+ * the caller's buffer for consistency checks like CRC could be clobbered by
+ * other threads. A thread-local buffer is also insufficient because some
+ * streams may call other streams in their skip() method, also clobbering the
+ * buffer.
+ */
+ public static long skipByReading(InputStream in, long byteCount) throws IOException {
+ // acquire the shared skip buffer.
+ byte[] buffer = skipBuffer.getAndSet(null);
+ if (buffer == null) {
+ buffer = new byte[4096];
+ }
+
+ long skipped = 0;
+ while (skipped < byteCount) {
+ int toRead = (int) Math.min(byteCount - skipped, buffer.length);
+ int read = in.read(buffer, 0, toRead);
+ if (read == -1) {
+ break;
+ }
+ skipped += read;
+ if (read < toRead) {
+ break;
+ }
+ }
+
+ // release the shared skip buffer.
+ skipBuffer.set(buffer);
+
+ return skipped;
+ }
+
+ /**
+ * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
+ * Returns the total number of bytes transferred.
+ */
+ public static int copy(InputStream in, OutputStream out) throws IOException {
+ int total = 0;
+ byte[] buffer = new byte[8192];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+ }
+ return total;
+ }
+
+ /**
+ * Returns the ASCII characters up to but not including the next "\r\n", or
+ * "\n".
+ *
+ * @throws java.io.EOFException if the stream is exhausted before the next newline
+ * character.
+ */
+ public static String readAsciiLine(InputStream in) throws IOException {
+ // TODO: support UTF-8 here instead
+
+ StringBuilder result = new StringBuilder(80);
+ while (true) {
+ int c = in.read();
+ if (c == -1) {
+ throw new EOFException();
+ } else if (c == '\n') {
+ break;
+ }
+
+ result.append((char) c);
+ }
+ int length = result.length();
+ if (length > 0 && result.charAt(length - 1) == '\r') {
+ result.setLength(length - 1);
+ }
+ return result.toString();
+ }
+}
diff --git a/src/main/java/libcore/net/MimeUtils.java b/src/main/java/libcore/net/MimeUtils.java
new file mode 100644
index 000000000000..f8038f0b651b
--- /dev/null
+++ b/src/main/java/libcore/net/MimeUtils.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2010 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 libcore.net;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Utilities for dealing with MIME types.
+ * Used to implement java.net.URLConnection and android.webkit.MimeTypeMap.
+ */
+public final class MimeUtils {
+ private static final Map mimeTypeToExtensionMap = new HashMap();
+
+ private static final Map extensionToMimeTypeMap = new HashMap();
+
+ static {
+ // The following table is based on /etc/mime.types data minus
+ // chemical/* MIME types and MIME types that don't map to any
+ // file extensions. We also exclude top-level domain names to
+ // deal with cases like:
+ //
+ // mail.google.com/a/google.com
+ //
+ // and "active" MIME types (due to potential security issues).
+
+ add("application/andrew-inset", "ez");
+ add("application/dsptype", "tsp");
+ add("application/futuresplash", "spl");
+ add("application/hta", "hta");
+ add("application/mac-binhex40", "hqx");
+ add("application/mac-compactpro", "cpt");
+ add("application/mathematica", "nb");
+ add("application/msaccess", "mdb");
+ add("application/oda", "oda");
+ add("application/ogg", "ogg");
+ add("application/pdf", "pdf");
+ add("application/pgp-keys", "key");
+ add("application/pgp-signature", "pgp");
+ add("application/pics-rules", "prf");
+ add("application/rar", "rar");
+ add("application/rdf+xml", "rdf");
+ add("application/rss+xml", "rss");
+ add("application/zip", "zip");
+ add("application/vnd.android.package-archive", "apk");
+ add("application/vnd.cinderella", "cdy");
+ add("application/vnd.ms-pki.stl", "stl");
+ add("application/vnd.oasis.opendocument.database", "odb");
+ add("application/vnd.oasis.opendocument.formula", "odf");
+ add("application/vnd.oasis.opendocument.graphics", "odg");
+ add("application/vnd.oasis.opendocument.graphics-template", "otg");
+ add("application/vnd.oasis.opendocument.image", "odi");
+ add("application/vnd.oasis.opendocument.spreadsheet", "ods");
+ add("application/vnd.oasis.opendocument.spreadsheet-template", "ots");
+ add("application/vnd.oasis.opendocument.text", "odt");
+ add("application/vnd.oasis.opendocument.text-master", "odm");
+ add("application/vnd.oasis.opendocument.text-template", "ott");
+ add("application/vnd.oasis.opendocument.text-web", "oth");
+ add("application/vnd.google-earth.kml+xml", "kml");
+ add("application/vnd.google-earth.kmz", "kmz");
+ add("application/msword", "doc");
+ add("application/msword", "dot");
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx");
+ add("application/vnd.ms-excel", "xls");
+ add("application/vnd.ms-excel", "xlt");
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx");
+ add("application/vnd.ms-powerpoint", "ppt");
+ add("application/vnd.ms-powerpoint", "pot");
+ add("application/vnd.ms-powerpoint", "pps");
+ add("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
+ add("application/vnd.openxmlformats-officedocument.presentationml.template", "potx");
+ add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx");
+ add("application/vnd.rim.cod", "cod");
+ add("application/vnd.smaf", "mmf");
+ add("application/vnd.stardivision.calc", "sdc");
+ add("application/vnd.stardivision.draw", "sda");
+ add("application/vnd.stardivision.impress", "sdd");
+ add("application/vnd.stardivision.impress", "sdp");
+ add("application/vnd.stardivision.math", "smf");
+ add("application/vnd.stardivision.writer", "sdw");
+ add("application/vnd.stardivision.writer", "vor");
+ add("application/vnd.stardivision.writer-global", "sgl");
+ add("application/vnd.sun.xml.calc", "sxc");
+ add("application/vnd.sun.xml.calc.template", "stc");
+ add("application/vnd.sun.xml.draw", "sxd");
+ add("application/vnd.sun.xml.draw.template", "std");
+ add("application/vnd.sun.xml.impress", "sxi");
+ add("application/vnd.sun.xml.impress.template", "sti");
+ add("application/vnd.sun.xml.math", "sxm");
+ add("application/vnd.sun.xml.writer", "sxw");
+ add("application/vnd.sun.xml.writer.global", "sxg");
+ add("application/vnd.sun.xml.writer.template", "stw");
+ add("application/vnd.visio", "vsd");
+ add("application/x-abiword", "abw");
+ add("application/x-apple-diskimage", "dmg");
+ add("application/x-bcpio", "bcpio");
+ add("application/x-bittorrent", "torrent");
+ add("application/x-cdf", "cdf");
+ add("application/x-cdlink", "vcd");
+ add("application/x-chess-pgn", "pgn");
+ add("application/x-cpio", "cpio");
+ add("application/x-debian-package", "deb");
+ add("application/x-debian-package", "udeb");
+ add("application/x-director", "dcr");
+ add("application/x-director", "dir");
+ add("application/x-director", "dxr");
+ add("application/x-dms", "dms");
+ add("application/x-doom", "wad");
+ add("application/x-dvi", "dvi");
+ add("application/x-flac", "flac");
+ add("application/x-font", "pfa");
+ add("application/x-font", "pfb");
+ add("application/x-font", "gsf");
+ add("application/x-font", "pcf");
+ add("application/x-font", "pcf.Z");
+ add("application/x-freemind", "mm");
+ add("application/x-futuresplash", "spl");
+ add("application/x-gnumeric", "gnumeric");
+ add("application/x-go-sgf", "sgf");
+ add("application/x-graphing-calculator", "gcf");
+ add("application/x-gtar", "gtar");
+ add("application/x-gtar", "tgz");
+ add("application/x-gtar", "taz");
+ add("application/x-hdf", "hdf");
+ add("application/x-ica", "ica");
+ add("application/x-internet-signup", "ins");
+ add("application/x-internet-signup", "isp");
+ add("application/x-iphone", "iii");
+ add("application/x-iso9660-image", "iso");
+ add("application/x-jmol", "jmz");
+ add("application/x-kchart", "chrt");
+ add("application/x-killustrator", "kil");
+ add("application/x-koan", "skp");
+ add("application/x-koan", "skd");
+ add("application/x-koan", "skt");
+ add("application/x-koan", "skm");
+ add("application/x-kpresenter", "kpr");
+ add("application/x-kpresenter", "kpt");
+ add("application/x-kspread", "ksp");
+ add("application/x-kword", "kwd");
+ add("application/x-kword", "kwt");
+ add("application/x-latex", "latex");
+ add("application/x-lha", "lha");
+ add("application/x-lzh", "lzh");
+ add("application/x-lzx", "lzx");
+ add("application/x-maker", "frm");
+ add("application/x-maker", "maker");
+ add("application/x-maker", "frame");
+ add("application/x-maker", "fb");
+ add("application/x-maker", "book");
+ add("application/x-maker", "fbdoc");
+ add("application/x-mif", "mif");
+ add("application/x-ms-wmd", "wmd");
+ add("application/x-ms-wmz", "wmz");
+ add("application/x-msi", "msi");
+ add("application/x-ns-proxy-autoconfig", "pac");
+ add("application/x-nwc", "nwc");
+ add("application/x-object", "o");
+ add("application/x-oz-application", "oza");
+ add("application/x-pkcs12", "p12");
+ add("application/x-pkcs12", "pfx");
+ add("application/x-pkcs7-certreqresp", "p7r");
+ add("application/x-pkcs7-crl", "crl");
+ add("application/x-quicktimeplayer", "qtl");
+ add("application/x-shar", "shar");
+ add("application/x-shockwave-flash", "swf");
+ add("application/x-stuffit", "sit");
+ add("application/x-sv4cpio", "sv4cpio");
+ add("application/x-sv4crc", "sv4crc");
+ add("application/x-tar", "tar");
+ add("application/x-texinfo", "texinfo");
+ add("application/x-texinfo", "texi");
+ add("application/x-troff", "t");
+ add("application/x-troff", "roff");
+ add("application/x-troff-man", "man");
+ add("application/x-ustar", "ustar");
+ add("application/x-wais-source", "src");
+ add("application/x-wingz", "wz");
+ add("application/x-webarchive", "webarchive");
+ add("application/x-webarchive-xml", "webarchivexml");
+ add("application/x-x509-ca-cert", "crt");
+ add("application/x-x509-user-cert", "crt");
+ add("application/x-xcf", "xcf");
+ add("application/x-xfig", "fig");
+ add("application/xhtml+xml", "xhtml");
+ add("audio/3gpp", "3gpp");
+ add("audio/amr", "amr");
+ add("audio/basic", "snd");
+ add("audio/midi", "mid");
+ add("audio/midi", "midi");
+ add("audio/midi", "kar");
+ add("audio/midi", "xmf");
+ add("audio/mobile-xmf", "mxmf");
+ add("audio/mpeg", "mpga");
+ add("audio/mpeg", "mpega");
+ add("audio/mpeg", "mp2");
+ add("audio/mpeg", "mp3");
+ add("audio/mpeg", "m4a");
+ add("audio/mpegurl", "m3u");
+ add("audio/prs.sid", "sid");
+ add("audio/x-aiff", "aif");
+ add("audio/x-aiff", "aiff");
+ add("audio/x-aiff", "aifc");
+ add("audio/x-gsm", "gsm");
+ add("audio/x-mpegurl", "m3u");
+ add("audio/x-ms-wma", "wma");
+ add("audio/x-ms-wax", "wax");
+ add("audio/x-pn-realaudio", "ra");
+ add("audio/x-pn-realaudio", "rm");
+ add("audio/x-pn-realaudio", "ram");
+ add("audio/x-realaudio", "ra");
+ add("audio/x-scpls", "pls");
+ add("audio/x-sd2", "sd2");
+ add("audio/x-wav", "wav");
+ add("image/bmp", "bmp");
+ add("image/gif", "gif");
+ add("image/ico", "cur");
+ add("image/ico", "ico");
+ add("image/ief", "ief");
+ add("image/jpeg", "jpeg");
+ add("image/jpeg", "jpg");
+ add("image/jpeg", "jpe");
+ add("image/pcx", "pcx");
+ add("image/png", "png");
+ add("image/svg+xml", "svg");
+ add("image/svg+xml", "svgz");
+ add("image/tiff", "tiff");
+ add("image/tiff", "tif");
+ add("image/vnd.djvu", "djvu");
+ add("image/vnd.djvu", "djv");
+ add("image/vnd.wap.wbmp", "wbmp");
+ add("image/x-cmu-raster", "ras");
+ add("image/x-coreldraw", "cdr");
+ add("image/x-coreldrawpattern", "pat");
+ add("image/x-coreldrawtemplate", "cdt");
+ add("image/x-corelphotopaint", "cpt");
+ add("image/x-icon", "ico");
+ add("image/x-jg", "art");
+ add("image/x-jng", "jng");
+ add("image/x-ms-bmp", "bmp");
+ add("image/x-photoshop", "psd");
+ add("image/x-portable-anymap", "pnm");
+ add("image/x-portable-bitmap", "pbm");
+ add("image/x-portable-graymap", "pgm");
+ add("image/x-portable-pixmap", "ppm");
+ add("image/x-rgb", "rgb");
+ add("image/x-xbitmap", "xbm");
+ add("image/x-xpixmap", "xpm");
+ add("image/x-xwindowdump", "xwd");
+ add("model/iges", "igs");
+ add("model/iges", "iges");
+ add("model/mesh", "msh");
+ add("model/mesh", "mesh");
+ add("model/mesh", "silo");
+ add("text/calendar", "ics");
+ add("text/calendar", "icz");
+ add("text/comma-separated-values", "csv");
+ add("text/css", "css");
+ add("text/html", "htm");
+ add("text/html", "html");
+ add("text/h323", "323");
+ add("text/iuls", "uls");
+ add("text/mathml", "mml");
+ // add ".txt" first so it will be the default for ExtensionFromMimeType
+ add("text/plain", "txt");
+ add("text/plain", "asc");
+ add("text/plain", "text");
+ add("text/plain", "diff");
+ add("text/plain", "po"); // reserve "pot" for vnd.ms-powerpoint
+ add("text/richtext", "rtx");
+ add("text/rtf", "rtf");
+ add("text/texmacs", "ts");
+ add("text/text", "phps");
+ add("text/tab-separated-values", "tsv");
+ add("text/xml", "xml");
+ add("text/x-bibtex", "bib");
+ add("text/x-boo", "boo");
+ add("text/x-c++hdr", "h++");
+ add("text/x-c++hdr", "hpp");
+ add("text/x-c++hdr", "hxx");
+ add("text/x-c++hdr", "hh");
+ add("text/x-c++src", "c++");
+ add("text/x-c++src", "cpp");
+ add("text/x-c++src", "cxx");
+ add("text/x-chdr", "h");
+ add("text/x-component", "htc");
+ add("text/x-csh", "csh");
+ add("text/x-csrc", "c");
+ add("text/x-dsrc", "d");
+ add("text/x-haskell", "hs");
+ add("text/x-java", "java");
+ add("text/x-literate-haskell", "lhs");
+ add("text/x-moc", "moc");
+ add("text/x-pascal", "p");
+ add("text/x-pascal", "pas");
+ add("text/x-pcs-gcd", "gcd");
+ add("text/x-setext", "etx");
+ add("text/x-tcl", "tcl");
+ add("text/x-tex", "tex");
+ add("text/x-tex", "ltx");
+ add("text/x-tex", "sty");
+ add("text/x-tex", "cls");
+ add("text/x-vcalendar", "vcs");
+ add("text/x-vcard", "vcf");
+ add("video/3gpp", "3gpp");
+ add("video/3gpp", "3gp");
+ add("video/3gpp", "3g2");
+ add("video/dl", "dl");
+ add("video/dv", "dif");
+ add("video/dv", "dv");
+ add("video/fli", "fli");
+ add("video/m4v", "m4v");
+ add("video/mpeg", "mpeg");
+ add("video/mpeg", "mpg");
+ add("video/mpeg", "mpe");
+ add("video/mp4", "mp4");
+ add("video/mpeg", "VOB");
+ add("video/quicktime", "qt");
+ add("video/quicktime", "mov");
+ add("video/vnd.mpegurl", "mxu");
+ add("video/x-la-asf", "lsf");
+ add("video/x-la-asf", "lsx");
+ add("video/x-mng", "mng");
+ add("video/x-ms-asf", "asf");
+ add("video/x-ms-asf", "asx");
+ add("video/x-ms-wm", "wm");
+ add("video/x-ms-wmv", "wmv");
+ add("video/x-ms-wmx", "wmx");
+ add("video/x-ms-wvx", "wvx");
+ add("video/x-msvideo", "avi");
+ add("video/x-sgi-movie", "movie");
+ add("x-conference/x-cooltalk", "ice");
+ add("x-epoc/x-sisx-app", "sisx");
+ applyOverrides();
+ }
+
+ private static void add(String mimeType, String extension) {
+ //
+ // if we have an existing x --> y mapping, we do not want to
+ // override it with another mapping x --> ?
+ // this is mostly because of the way the mime-type map below
+ // is constructed (if a mime type maps to several extensions
+ // the first extension is considered the most popular and is
+ // added first; we do not want to overwrite it later).
+ //
+ if (!mimeTypeToExtensionMap.containsKey(mimeType)) {
+ mimeTypeToExtensionMap.put(mimeType, extension);
+ }
+ extensionToMimeTypeMap.put(extension, mimeType);
+ }
+
+ private static InputStream getContentTypesPropertiesStream() {
+ // User override?
+ String userTable = System.getProperty("content.types.user.table");
+ if (userTable != null) {
+ File f = new File(userTable);
+ if (f.exists()) {
+ try {
+ return new FileInputStream(f);
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ // Standard location?
+ File f = new File(System.getProperty("java.home"), "lib" + File.separator + "content-types.properties");
+ if (f.exists()) {
+ try {
+ return new FileInputStream(f);
+ } catch (IOException ignored) {
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your
+ * own "content.types.user.table" means you don't get any of the built-ins, and the built-ins
+ * come from "$JAVA_HOME/lib/content-types.properties".
+ */
+ private static void applyOverrides() {
+ // Get the appropriate InputStream to read overrides from, if any.
+ InputStream stream = getContentTypesPropertiesStream();
+ if (stream == null) {
+ return;
+ }
+
+ try {
+ try {
+ // Read the properties file...
+ Properties overrides = new Properties();
+ overrides.load(stream);
+ // And translate its mapping to ours...
+ for (Map.Entry