am ee100f24: Merge "Make ErrnoException a checked exception."

* commit 'ee100f24d3b9d538dfc0ec9c9e74b6670e0a2a1f':
  Make ErrnoException a checked exception.
diff --git a/luni/src/main/java/java/net/URI.java b/luni/src/main/java/java/net/URI.java
index 352e25f..6b7b1da 100644
--- a/luni/src/main/java/java/net/URI.java
+++ b/luni/src/main/java/java/net/URI.java
@@ -26,8 +26,108 @@
 import libcore.net.url.UrlUtils;
 
 /**
- * This class represents an instance of a URI as defined by
- * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
+ * A Uniform Resource Identifier that identifies an abstract or physical
+ * resource, as specified by <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC
+ * 2396</a>.
+ *
+ * <h3>Parts of a URI</h3>
+ * A URI is composed of many parts. This class can both parse URI strings into
+ * parts and compose URI strings from parts. For example, consider the parts of
+ * this URI:
+ * {@code http://username:password@host:8080/directory/file?query#fragment}
+ * <table>
+ * <tr><th>Component                                            </th><th>Example value                                                      </th><th>Also known as</th></tr>
+ * <tr><td>{@link #getScheme() Scheme}                          </td><td>{@code http}                                                       </td><td>protocol</td></tr>
+ * <tr><td>{@link #getSchemeSpecificPart() Scheme-specific part}</td><td>{@code //username:password@host:8080/directory/file?query#fragment}</td><td></td></tr>
+ * <tr><td>{@link #getAuthority() Authority}                    </td><td>{@code username:password@host:8080}                                </td><td></td></tr>
+ * <tr><td>{@link #getUserInfo() User Info}                     </td><td>{@code username:password}                                          </td><td></td></tr>
+ * <tr><td>{@link #getHost() Host}                              </td><td>{@code host}                                                       </td><td></td></tr>
+ * <tr><td>{@link #getPort() Port}                              </td><td>{@code 8080}                                                       </td><td></td></tr>
+ * <tr><td>{@link #getPath() Path}                              </td><td>{@code /directory/file}                                            </td><td></td></tr>
+ * <tr><td>{@link #getQuery() Query}                            </td><td>{@code query}                                                      </td><td></td></tr>
+ * <tr><td>{@link #getFragment() Fragment}                      </td><td>{@code fragment}                                                   </td><td>ref</td></tr>
+ * </table>
+ *
+ * <h3>Absolute vs. Relative URIs</h3>
+ * URIs are either {@link #isAbsolute() absolute or relative}.
+ * <ul>
+ *     <li><strong>Absolute:</strong> {@code http://android.com/robots.txt}
+ *     <li><strong>Relative:</strong> {@code robots.txt}
+ * </ul>
+ *
+ * <p>Absolute URIs always have a scheme. If its scheme is supported by {@link
+ * URL}, you can use {@link #toURL} to convert an absolute URI to a URL.
+ *
+ * <p>Relative URIs do not have a scheme and cannot be converted to URLs. If you
+ * have the absolute URI that a relative URI is relative to, you can use {@link
+ * #resolve} to compute the referenced absolute URI. Symmetrically, you can use
+ * {@link #relativize} to compute the relative URI from one URI to another.
+ * <pre>   {@code
+ *   URI absolute = new URI("http://android.com/");
+ *   URI relative = new URI("robots.txt");
+ *   URI resolved = new URI("http://android.com/robots.txt");
+ *
+ *   // print "http://android.com/robots.txt"
+ *   System.out.println(absolute.resolve(relative));
+ *
+ *   // print "robots.txt"
+ *   System.out.println(absolute.relativize(resolved));
+ * }</pre>
+ *
+ * <h3>Opaque vs. Hierarchical URIs</h3>
+ * Absolute URIs are either {@link #isOpaque() opaque or hierarchical}. Relative
+ * URIs are always hierarchical.
+ * <ul>
+ *     <li><strong>Hierarchical:</strong> {@code http://android.com/robots.txt}
+ *     <li><strong>Opaque:</strong> {@code mailto:robots@example.com}
+ * </ul>
+ *
+ * <p>Opaque URIs have both a scheme and a scheme-specific part that does not
+ * begin with the slash character: {@code /}. The contents of the
+ * scheme-specific part of an opaque URI is not parsed so an opaque URI never
+ * has an authority, user info, host, port, path or query. An opaque URIs may
+ * have a fragment, however. A typical opaque URI is
+ * {@code mailto:robots@example.com}.
+ * <table>
+ * <tr><th>Component           </th><th>Example value             </th></tr>
+ * <tr><td>Scheme              </td><td>{@code mailto}            </td></tr>
+ * <tr><td>Scheme-specific part</td><td>{@code robots@example.com}</td></tr>
+ * <tr><td>Fragment            </td><td>                          </td></tr>
+ * </table>
+ * <p>Hierarchical URIs may have values for any URL component. They always
+ * have a non-null path, though that path may be the empty string.
+ *
+ * <h3>Encoding and Decoding URI Components</h3>
+ * Each component of a URI permits a limited set of legal characters. Other
+ * characters must first be <i>encoded</i> before they can be embedded in a URI.
+ * To recover the original characters from a URI, they may be <i>decoded</i>.
+ * <strong>Contrary to what you might expect,</strong> this class uses the
+ * term <i>raw</i> to refer to encoded strings. The non-<i>raw</i> accessors
+ * return decoded strings. For example, consider how this URI is decoded:
+ * {@code http://user:pa55w%3Frd@host:80/doc%7Csearch?q=green%20robots#over%206%22}
+ * <table>
+ * <tr><th>Component           </th><th>Legal Characters                                                    </th><th>Other Constraints                                  </th><th>Raw Value                                                      </th><th>Value</th></tr>
+ * <tr><td>Scheme              </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code +-.}                  </td><td>First character must be in {@code a-z}, {@code A-Z}</td><td>                                                               </td><td>{@code http}</td></tr>
+ * <tr><td>Scheme-specific part</td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=?/[]@}</td><td>Non-ASCII characters okay                          </td><td>{@code //user:pa55w%3Frd@host:80/doc%7Csearch?q=green%20robots}</td><td>{@code //user:pa55w?rd@host:80/doc|search?q=green robots}</td></tr>
+ * <tr><td>Authority           </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=@[]}  </td><td>Non-ASCII characters okay                          </td><td>{@code user:pa55w%3Frd@host:80}                                </td><td>{@code user:pa55w?rd@host:80}</td></tr>
+ * <tr><td>User Info           </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=}     </td><td>Non-ASCII characters okay                          </td><td>{@code user:pa55w%3Frd}                                        </td><td>{@code user:pa55w?rd}</td></tr>
+ * <tr><td>Host                </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code -.[]}                 </td><td>Domain name, IPv4 address or [IPv6 address]        </td><td>                                                               </td><td>host</td></tr>
+ * <tr><td>Port                </td><td>{@code 0-9}                                                         </td><td>                                                   </td><td>                                                               </td><td>{@code 80}</td></tr>
+ * <tr><td>Path                </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=/@}   </td><td>Non-ASCII characters okay                          </td><td>{@code /doc%7Csearch}                                          </td><td>{@code /doc|search}</td></tr>
+ * <tr><td>Query               </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=?/[]@}</td><td>Non-ASCII characters okay                          </td><td>{@code q=green%20robots}                                       </td><td>{@code q=green robots}</td></tr>
+ * <tr><td>Fragment            </td><td>{@code 0-9}, {@code a-z}, {@code A-Z}, {@code _-!.~'()*,;:$&+=?/[]@}</td><td>Non-ASCII characters okay                          </td><td>{@code over%206%22}                                            </td><td>{@code over 6"}</td></tr>
+ * </table>
+ * A URI's host, port and scheme are not eligible for encoding and must not
+ * contain illegal characters.
+ *
+ * <p>To encode a URI, invoke any of the multiple-parameter constructors of this
+ * class. These constructors accept your original strings and encode them into
+ * their raw form.
+ *
+ * <p>To decode a URI, invoke the single-string constructor, and then use the
+ * appropriate accessor methods to get the decoded components.
+ *
+ * <p>The {@link URL} class can be used to retrieve resources by their URI.
  */
 public final class URI implements Comparable<URI>, Serializable {
 
@@ -96,94 +196,51 @@
     private URI() {}
 
     /**
-     * Creates a new URI instance according to the given string {@code uri}.
+     * Creates a new URI instance by parsing {@code spec}.
      *
-     * @param uri
-     *            the textual URI representation to be parsed into a URI object.
-     * @throws URISyntaxException
-     *         if the given {@code uri} isn't an
-     *         <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> URI
-     *         or could not be parsed correctly.
+     * @param spec a URI whose illegal characters have all been encoded.
      */
-    public URI(String uri) throws URISyntaxException {
-        parseURI(uri, false);
+    public URI(String spec) throws URISyntaxException {
+        parseURI(spec, false);
     }
 
     /**
-     * Creates a new URI instance using the given arguments. This constructor
-     * first creates a temporary URI string from the given components. This
-     * string will be parsed later on to create the URI instance.
-     * <p>
-     * {@code [scheme:]scheme-specific-part[#fragment]}
+     * Creates a new URI instance of the given unencoded component parts.
      *
-     * @param scheme
-     *            the scheme part of the URI.
-     * @param ssp
-     *            the scheme-specific-part of the URI.
-     * @param frag
-     *            the fragment part of the URI.
-     * @throws URISyntaxException
-     *         if the resulting URI isn't an
-     *         <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> URI
-     *         or could not be parsed correctly.
+     * @param scheme the URI scheme, or null for a non-absolute URI.
      */
-    public URI(String scheme, String ssp, String frag) throws URISyntaxException {
+    public URI(String scheme, String schemeSpecificPart, String fragment)
+            throws URISyntaxException {
         StringBuilder uri = new StringBuilder();
         if (scheme != null) {
             uri.append(scheme);
             uri.append(':');
         }
-        if (ssp != null) {
-            ALL_LEGAL_ENCODER.appendEncoded(uri, ssp);
+        if (schemeSpecificPart != null) {
+            ALL_LEGAL_ENCODER.appendEncoded(uri, schemeSpecificPart);
         }
-        if (frag != null) {
+        if (fragment != null) {
             uri.append('#');
-            ALL_LEGAL_ENCODER.appendEncoded(uri, frag);
+            ALL_LEGAL_ENCODER.appendEncoded(uri, fragment);
         }
 
         parseURI(uri.toString(), false);
     }
 
     /**
-     * Creates a new URI instance using the given arguments. This constructor
-     * first creates a temporary URI string from the given components. This
-     * string will be parsed later on to create the URI instance.
-     * <p>
-     * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
+     * Creates a new URI instance of the given unencoded component parts.
      *
-     * @param scheme
-     *            the scheme part of the URI.
-     * @param userInfo
-     *            the user information of the URI for authentication and
-     *            authorization.
-     * @param host
-     *            the host name of the URI.
-     * @param port
-     *            the port number of the URI.
-     * @param path
-     *            the path to the resource on the host.
-     * @param query
-     *            the query part of the URI to specify parameters for the
-     *            resource.
-     * @param fragment
-     *            the fragment part of the URI.
-     * @throws URISyntaxException
-     *         if the resulting URI isn't an
-     *         <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> URI
-     *         or could not be parsed correctly.
+     * @param scheme the URI scheme, or null for a non-absolute URI.
      */
-    public URI(String scheme, String userInfo, String host, int port,
-            String path, String query, String fragment)
-            throws URISyntaxException {
-
+    public URI(String scheme, String userInfo, String host, int port, String path, String query,
+            String fragment) throws URISyntaxException {
         if (scheme == null && userInfo == null && host == null && path == null
                 && query == null && fragment == null) {
             this.path = "";
             return;
         }
 
-        if (scheme != null && path != null && path.length() > 0
-                && path.charAt(0) != '/') {
+        if (scheme != null && path != null && !path.isEmpty() && path.charAt(0) != '/') {
             throw new URISyntaxException(path, "Relative path");
         }
 
@@ -203,10 +260,8 @@
         }
 
         if (host != null) {
-            // check for IPv6 addresses that hasn't been enclosed
-            // in square brackets
-            if (host.indexOf(':') != -1 && host.indexOf(']') == -1
-                    && host.indexOf('[') == -1) {
+            // check for IPv6 addresses that hasn't been enclosed in square brackets
+            if (host.indexOf(':') != -1 && host.indexOf(']') == -1 && host.indexOf('[') == -1) {
                 host = "[" + host + "]";
             }
             uri.append(host);
@@ -235,56 +290,22 @@
     }
 
     /**
-     * Creates a new URI instance using the given arguments. This constructor
-     * first creates a temporary URI string from the given components. This
-     * string will be parsed later on to create the URI instance.
-     * <p>
-     * {@code [scheme:]host[path][#fragment]}
+     * Creates a new URI instance of the given unencoded component parts.
      *
-     * @param scheme
-     *            the scheme part of the URI.
-     * @param host
-     *            the host name of the URI.
-     * @param path
-     *            the path to the resource on the host.
-     * @param fragment
-     *            the fragment part of the URI.
-     * @throws URISyntaxException
-     *         if the resulting URI isn't an
-     *         <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> URI
-     *         or could not be parsed correctly.
+     * @param scheme the URI scheme, or null for a non-absolute URI.
      */
     public URI(String scheme, String host, String path, String fragment) throws URISyntaxException {
         this(scheme, null, host, -1, path, null, fragment);
     }
 
     /**
-     * Creates a new URI instance using the given arguments. This constructor
-     * first creates a temporary URI string from the given components. This
-     * string will be parsed later on to create the URI instance.
-     * <p>
-     * {@code [scheme:][//authority][path][?query][#fragment]}
+     * Creates a new URI instance of the given unencoded component parts.
      *
-     * @param scheme
-     *            the scheme part of the URI.
-     * @param authority
-     *            the authority part of the URI.
-     * @param path
-     *            the path to the resource on the host.
-     * @param query
-     *            the query part of the URI to specify parameters for the
-     *            resource.
-     * @param fragment
-     *            the fragment part of the URI.
-     * @throws URISyntaxException
-     *         if the resulting URI isn't an
-     *         <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> URI
-     *         or could not be parsed correctly.
+     * @param scheme the URI scheme, or null for a non-absolute URI.
      */
     public URI(String scheme, String authority, String path, String query,
             String fragment) throws URISyntaxException {
-        if (scheme != null && path != null && path.length() > 0
-                && path.charAt(0) != '/') {
+        if (scheme != null && path != null && !path.isEmpty() && path.charAt(0) != '/') {
             throw new URISyntaxException(path, "Relative path");
         }
 
@@ -774,18 +795,7 @@
         return first.substring(prevIndex).equals(second.substring(prevIndex));
     }
 
-    /**
-     * Compares this URI instance with the given argument {@code o} and
-     * determines if both are equal. Two URI instances are equal if all single
-     * parts are identical in their meaning.
-     *
-     * @param o
-     *            the URI this instance has to be compared with.
-     * @return {@code true} if both URI instances point to the same resource,
-     *         {@code false} otherwise.
-     */
-    @Override
-    public boolean equals(Object o) {
+    @Override public boolean equals(Object o) {
         if (!(o instanceof URI)) {
             return false;
         }
@@ -866,45 +876,71 @@
     }
 
     /**
-     * Gets the decoded authority part of this URI.
-     *
-     * @return the decoded authority part or {@code null} if undefined.
+     * Returns the scheme of this URI, or null if this URI has no scheme. This
+     * is also known as the protocol.
+     */
+    public String getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Returns the decoded scheme-specific part of this URI, or null if this URI
+     * has no scheme-specific part.
+     */
+    public String getSchemeSpecificPart() {
+        return decode(schemeSpecificPart);
+    }
+
+    /**
+     * Returns the encoded scheme-specific part of this URI, or null if this URI
+     * has no scheme-specific part.
+     */
+    public String getRawSchemeSpecificPart() {
+        return schemeSpecificPart;
+    }
+
+    /**
+     * Returns the decoded authority part of this URI, or null if this URI has
+     * no authority.
      */
     public String getAuthority() {
         return decode(authority);
     }
 
     /**
-     * Gets the decoded fragment part of this URI.
-     *
-     * @return the decoded fragment part or {@code null} if undefined.
+     * Returns the encoded authority of this URI, or null if this URI has no
+     * authority.
      */
-    public String getFragment() {
-        return decode(fragment);
+    public String getRawAuthority() {
+        return authority;
     }
 
     /**
-     * Gets the host part of this URI.
-     *
-     * @return the host part or {@code null} if undefined.
+     * Returns the decoded user info of this URI, or null if this URI has no
+     * user info.
+     */
+    public String getUserInfo() {
+        return decode(userInfo);
+    }
+
+    /**
+     * Returns the encoded user info of this URI, or null if this URI has no
+     * user info.
+     */
+    public String getRawUserInfo() {
+        return userInfo;
+    }
+
+    /**
+     * Returns the host of this URI, or null if this URI has no host.
      */
     public String getHost() {
         return host;
     }
 
     /**
-     * Gets the decoded path part of this URI.
-     *
-     * @return the decoded path part or {@code null} if undefined.
-     */
-    public String getPath() {
-        return decode(path);
-    }
-
-    /**
-     * Gets the port number of this URI.
-     *
-     * @return the port number or {@code -1} if undefined.
+     * Returns the port number of this URI, or {@code -1} if this URI has no
+     * explicit port.
      */
     public int getPort() {
         return port;
@@ -936,102 +972,50 @@
     }
 
     /**
-     * Gets the decoded query part of this URI.
-     *
-     * @return the decoded query part or {@code null} if undefined.
+     * Returns the decoded path of this URI, or null if this URI has no path.
      */
-    public String getQuery() {
-        return decode(query);
+    public String getPath() {
+        return decode(path);
     }
 
     /**
-     * Gets the authority part of this URI in raw form.
-     *
-     * @return the encoded authority part or {@code null} if undefined.
-     */
-    public String getRawAuthority() {
-        return authority;
-    }
-
-    /**
-     * Gets the fragment part of this URI in raw form.
-     *
-     * @return the encoded fragment part or {@code null} if undefined.
-     */
-    public String getRawFragment() {
-        return fragment;
-    }
-
-    /**
-     * Gets the path part of this URI in raw form.
-     *
-     * @return the encoded path part or {@code null} if undefined.
+     * Returns the encoded path of this URI, or null if this URI has no path.
      */
     public String getRawPath() {
         return path;
     }
 
     /**
-     * Gets the query part of this URI in raw form.
-     *
-     * @return the encoded query part or {@code null} if undefined.
+     * Returns the decoded query of this URI, or null if this URI has no query.
+     */
+    public String getQuery() {
+        return decode(query);
+    }
+
+    /**
+     * Returns the encoded query of this URI, or null if this URI has no query.
      */
     public String getRawQuery() {
         return query;
     }
 
     /**
-     * Gets the scheme-specific part of this URI in raw form.
-     *
-     * @return the encoded scheme-specific part or {@code null} if undefined.
+     * Returns the decoded fragment of this URI, or null if this URI has no
+     * fragment.
      */
-    public String getRawSchemeSpecificPart() {
-        return schemeSpecificPart;
+    public String getFragment() {
+        return decode(fragment);
     }
 
     /**
-     * Gets the user-info part of this URI in raw form.
-     *
-     * @return the encoded user-info part or {@code null} if undefined.
+     * Gets the encoded fragment of this URI, or null if this URI has no
+     * fragment.
      */
-    public String getRawUserInfo() {
-        return userInfo;
+    public String getRawFragment() {
+        return fragment;
     }
 
-    /**
-     * Gets the scheme part of this URI.
-     *
-     * @return the scheme part or {@code null} if undefined.
-     */
-    public String getScheme() {
-        return scheme;
-    }
-
-    /**
-     * Gets the decoded scheme-specific part of this URI.
-     *
-     * @return the decoded scheme-specific part or {@code null} if undefined.
-     */
-    public String getSchemeSpecificPart() {
-        return decode(schemeSpecificPart);
-    }
-
-    /**
-     * Gets the decoded user-info part of this URI.
-     *
-     * @return the decoded user-info part or {@code null} if undefined.
-     */
-    public String getUserInfo() {
-        return decode(userInfo);
-    }
-
-    /**
-     * Gets the hashcode value of this URI instance.
-     *
-     * @return the appropriate hashcode value.
-     */
-    @Override
-    public int hashCode() {
+    @Override public int hashCode() {
         if (hash == -1) {
             hash = getHashString().hashCode();
         }
@@ -1039,22 +1023,18 @@
     }
 
     /**
-     * Indicates whether this URI is absolute, which means that a scheme part is
-     * defined in this URI.
-     *
-     * @return {@code true} if this URI is absolute, {@code false} otherwise.
+     * Returns true if this URI is absolute, which means that a scheme is
+     * defined.
      */
     public boolean isAbsolute() {
+        // TODO: simplify to 'scheme != null' ?
         return absolute;
     }
 
     /**
-     * Indicates whether this URI is opaque or not. An opaque URI is absolute
-     * and has a scheme-specific part which does not start with a slash
-     * character. All parts except scheme, scheme-specific and fragment are
-     * undefined.
-     *
-     * @return {@code true} if the URI is opaque, {@code false} otherwise.
+     * Returns true if this URI is opaque. Opaque URIs are absolute and have a
+     * scheme-specific part that does not start with a slash character. All
+     * parts except scheme, scheme-specific and fragment are undefined.
      */
     public boolean isOpaque() {
         return opaque;
@@ -1276,43 +1256,42 @@
     }
 
     /**
-     * Returns the textual string representation of this URI instance.
-     *
-     * @return the textual string representation of this URI.
+     * Returns the encoded URI.
      */
-    @Override
-    public String toString() {
-        if (string == null) {
-            StringBuilder result = new StringBuilder();
-            if (scheme != null) {
-                result.append(scheme);
-                result.append(':');
-            }
-            if (opaque) {
-                result.append(schemeSpecificPart);
-            } else {
-                if (authority != null) {
-                    result.append("//");
-                    result.append(authority);
-                }
-
-                if (path != null) {
-                    result.append(path);
-                }
-
-                if (query != null) {
-                    result.append('?');
-                    result.append(query);
-                }
-            }
-
-            if (fragment != null) {
-                result.append('#');
-                result.append(fragment);
-            }
-
-            string = result.toString();
+    @Override public String toString() {
+        if (string != null) {
+            return string;
         }
+
+        StringBuilder result = new StringBuilder();
+        if (scheme != null) {
+            result.append(scheme);
+            result.append(':');
+        }
+        if (opaque) {
+            result.append(schemeSpecificPart);
+        } else {
+            if (authority != null) {
+                result.append("//");
+                result.append(authority);
+            }
+
+            if (path != null) {
+                result.append(path);
+            }
+
+            if (query != null) {
+                result.append('?');
+                result.append(query);
+            }
+        }
+
+        if (fragment != null) {
+            result.append('#');
+            result.append(fragment);
+        }
+
+        string = result.toString();
         return string;
     }
 
@@ -1378,8 +1357,7 @@
         return new URL(toString());
     }
 
-    private void readObject(ObjectInputStream in) throws IOException,
-            ClassNotFoundException {
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
         try {
             parseURI(string, false);
@@ -1388,8 +1366,7 @@
         }
     }
 
-    private void writeObject(ObjectOutputStream out) throws IOException,
-            ClassNotFoundException {
+    private void writeObject(ObjectOutputStream out) throws IOException, ClassNotFoundException {
         // call toString() to ensure the value of string field is calculated
         toString();
         out.defaultWriteObject();
diff --git a/luni/src/main/java/java/net/URL.java b/luni/src/main/java/java/net/URL.java
index 91a398e..798fee7 100644
--- a/luni/src/main/java/java/net/URL.java
+++ b/luni/src/main/java/java/net/URL.java
@@ -248,20 +248,14 @@
         this.host = host;
         this.port = port;
 
-        /*
-         * Force the path to start with a '/' if this URL has an authority.
-         * Otherwise they blend together like http://android.comindex.html.
-         */
-        if (host != null && !host.isEmpty() && !file.startsWith("/")) {
-            file = "/" + file;
-        }
+        file = UrlUtils.authoritySafePath(host, file);
 
         // Set the fields from the arguments. Handle the case where the
         // passed in "file" includes both a file and a reference part.
-        int index = file.indexOf("#", file.lastIndexOf("/"));
-        if (index != -1) {
-            this.file = file.substring(0, index);
-            ref = file.substring(index + 1);
+        int hash = file.indexOf("#");
+        if (hash != -1) {
+            this.file = file.substring(0, hash);
+            this.ref = file.substring(hash + 1);
         } else {
             this.file = file;
         }
diff --git a/luni/src/test/java/libcore/java/net/URITest.java b/luni/src/test/java/libcore/java/net/URITest.java
index 3a419c0..d45facc 100644
--- a/luni/src/test/java/libcore/java/net/URITest.java
+++ b/luni/src/test/java/libcore/java/net/URITest.java
@@ -366,7 +366,7 @@
         assertEquals("http://a/b/c/g/", base.resolve("g/").toString());
         assertEquals("http://a/g", base.resolve("/g").toString());
         assertEquals("http://g", base.resolve("//g").toString());
-        assertEquals("http://a/b/c/d;p?y", base.resolve("?y").toString()); // fails on RI
+        assertEquals("http://a/b/c/d;p?y", base.resolve("?y").toString()); // RI fails; loses file
         assertEquals("http://a/b/c/g?y", base.resolve("g?y").toString());
         assertEquals("http://a/b/c/d;p?q#s", base.resolve("#s").toString());
         assertEquals("http://a/b/c/g#s", base.resolve("g#s").toString());
@@ -387,13 +387,13 @@
 
     public void testRfc1808AbnormalExampleTooManyDotDotSequences() throws Exception {
         URI base = new URI("http://a/b/c/d;p?q");
-        assertEquals("http://a/g", base.resolve("../../../g").toString()); // fails on RI
+        assertEquals("http://a/g", base.resolve("../../../g").toString()); // RI doesn't normalize
         assertEquals("http://a/g", base.resolve("../../../../g").toString()); // fails on RI
     }
 
     public void testRfc1808AbnormalExampleRemoveDotSegments() throws Exception {
         URI base = new URI("http://a/b/c/d;p?q");
-        assertEquals("http://a/g", base.resolve("/./g").toString()); // fails on RI
+        assertEquals("http://a/g", base.resolve("/./g").toString()); // RI doesn't normalize
         assertEquals("http://a/g", base.resolve("/../g").toString()); // fails on RI
         assertEquals("http://a/b/c/g.", base.resolve("g.").toString());
         assertEquals("http://a/b/c/.g", base.resolve(".g").toString());
@@ -508,7 +508,7 @@
     public void testRelativize() throws Exception {
         URI a = new URI("http://host/a/b");
         URI b = new URI("http://host/a/b/c");
-        assertEquals("b/c", a.relativize(b).toString()); // fails on both the RI and libcore
+        assertEquals("b/c", a.relativize(b).toString()); // RI assumes a directory
     }
 
     public void testParseServerAuthorityInvalidAuthority() throws Exception {
@@ -531,9 +531,95 @@
         assertEquals(-1, uri.getPort());
     }
 
-    // TODO: test http://#fragment
-    // TODO: test http://?query
-    // TODO: test http:///path
+    public void testEncodingParts() throws Exception {
+        URI uri = new URI("http", "user:pa55w?rd", "host", 80, "/doc|search",
+                "q=green robots", "over 6\"");
+        assertEquals("http", uri.getScheme());
+        assertEquals("user:pa55w?rd@host:80", uri.getAuthority());
+        assertEquals("user:pa55w%3Frd@host:80", uri.getRawAuthority());
+        assertEquals("user:pa55w?rd", uri.getUserInfo());
+        assertEquals("user:pa55w%3Frd", uri.getRawUserInfo());
+        assertEquals("/doc|search", uri.getPath());
+        assertEquals("/doc%7Csearch", uri.getRawPath());
+        assertEquals("q=green robots", uri.getQuery());
+        assertEquals("q=green%20robots", uri.getRawQuery());
+        assertEquals("over 6\"", uri.getFragment());
+        assertEquals("over%206%22", uri.getRawFragment());
+        assertEquals("//user:pa55w?rd@host:80/doc|search?q=green robots",
+                uri.getSchemeSpecificPart());
+        assertEquals("//user:pa55w%3Frd@host:80/doc%7Csearch?q=green%20robots",
+                uri.getRawSchemeSpecificPart());
+        assertEquals("http://user:pa55w%3Frd@host:80/doc%7Csearch?q=green%20robots#over%206%22",
+                uri.toString());
+    }
+
+    public void testSchemeCaseIsNotCanonicalized() throws Exception {
+        URI uri = new URI("HTTP://host/path");
+        assertEquals("HTTP", uri.getScheme());
+    }
+
+    public void testEmptyAuthorityWithPath() throws Exception {
+        URI uri = new URI("http:///path");
+        assertEquals(null, uri.getAuthority());
+        assertEquals("/path", uri.getPath());
+    }
+
+    public void testEmptyAuthorityWithQuery() throws Exception {
+        URI uri = new URI("http://?query");
+        assertEquals(null, uri.getAuthority());
+        assertEquals("", uri.getPath());
+        assertEquals("query", uri.getQuery());
+    }
+
+    public void testEmptyAuthorityWithFragment() throws Exception {
+        URI uri = new URI("http://#fragment");
+        assertEquals(null, uri.getAuthority());
+        assertEquals("", uri.getPath());
+        assertEquals("fragment", uri.getFragment());
+    }
+
+    public void testEncodingConstructorsRefuseRelativePath() throws Exception {
+        try {
+            new URI("http", "host", "relative", null);
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http", "host", "relative", null, null);
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+        try {
+            new URI("http", null, "host", -1, "relative", null, null);
+            fail();
+        } catch (URISyntaxException expected) {
+        }
+    }
+
+    public void testEncodingConstructorsAcceptEmptyPath() throws Exception {
+        assertEquals("", new URI("http", "host", "", null).getPath());
+        assertEquals("", new URI("http", "host", "", null, null).getPath());
+        assertEquals("", new URI("http", null, "host", -1, "", null, null).getPath());
+    }
+
+    public void testResolveRelativeAndAbsolute() throws Exception {
+        URI absolute = new URI("http://android.com/");
+        URI relative = new URI("robots.txt");
+        assertEquals(absolute, absolute.resolve(absolute));
+        assertEquals(new URI("http://android.com/robots.txt"), absolute.resolve(relative));
+        assertEquals(absolute, relative.resolve(absolute));
+        assertEquals(relative, relative.resolve(relative));
+    }
+
+    public void testRelativizeRelativeAndAbsolute() throws Exception {
+        URI absolute = new URI("http://android.com/");
+        URI relative = new URI("robots.txt");
+        assertEquals(relative, absolute.relativize(new URI("http://android.com/robots.txt")));
+        assertEquals(new URI(""), absolute.relativize(absolute));
+        assertEquals(relative, absolute.relativize(relative));
+        assertEquals(absolute, relative.relativize(absolute));
+        assertEquals(new URI(""), relative.relativize(relative));
+    }
 
     // Adding a new test? Consider adding an equivalent test to URLTest.java
 }
diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index d57db07..946ac5c 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -19,6 +19,8 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import junit.framework.TestCase;
 import libcore.java.util.SerializableTester;
@@ -59,7 +61,7 @@
             URL urlByHostName = new URL("http://localhost/foo?bar=baz#quux");
             URL urlByAddress = new URL("http://" + address + "/foo?bar=baz#quux");
             assertFalse("Expected " + urlByHostName + " to not equal " + urlByAddress,
-                    urlByHostName.equals(urlByAddress)); // fails on RI
+                    urlByHostName.equals(urlByAddress)); // fails on RI, which does DNS
         }
     }
 
@@ -321,6 +323,13 @@
         assertEquals("fragment/path", url.getRef());
     }
 
+    public void testSlashInFragmentCombiningConstructor() throws Exception {
+        URL url = new URL("http", "host", "/file#fragment/path");
+        assertEquals("/file", url.getFile());
+        assertEquals("/file", url.getPath());
+        assertEquals("fragment/path", url.getRef());
+    }
+
     public void testHashInFragment() throws Exception {
         URL url = new URL("http://host/file#fragment#another");
         assertEquals("/file", url.getFile());
@@ -426,7 +435,7 @@
 
     public void testMalformedUrlsRefusedByFirefoxAndChrome() throws Exception {
         URL base = new URL("http://host/a/b/c");
-        assertEquals("http://", new URL(base, "http://").toString()); // fails on RI
+        assertEquals("http://", new URL(base, "http://").toString()); // fails on RI; path retained
         assertEquals("http://", new URL(base, "//").toString()); // fails on RI
         assertEquals("https:", new URL(base, "https:").toString());
         assertEquals("https:/", new URL(base, "https:/").toString());
@@ -441,7 +450,7 @@
         assertEquals("http://a/b/c/g/", new URL(base, "g/").toString());
         assertEquals("http://a/g", new URL(base, "/g").toString());
         assertEquals("http://g", new URL(base, "//g").toString());
-        assertEquals("http://a/b/c/d;p?y", new URL(base, "?y").toString()); // fails on RI
+        assertEquals("http://a/b/c/d;p?y", new URL(base, "?y").toString()); // RI fails; file lost
         assertEquals("http://a/b/c/g?y", new URL(base, "g?y").toString());
         assertEquals("http://a/b/c/d;p?q#s", new URL(base, "#s").toString());
         assertEquals("http://a/b/c/g#s", new URL(base, "g#s").toString());
@@ -511,7 +520,7 @@
         assertEquals("http", url.getProtocol());
         assertEquals("host", url.getAuthority());
         assertEquals("host", url.getHost());
-        assertEquals("/a", url.getFile()); // fails on RI
+        assertEquals("/a", url.getFile()); // RI fails; doesn't insert '/' separator
         assertEquals("http://host/a", url.toString()); // fails on RI
     }
 
@@ -602,5 +611,52 @@
         assertFalse(new URL("http://android.com").equals(new URL("http://android.com/")));
     }
 
+    public void testUrlDoesNotEncodeParts() throws Exception {
+        URL url = new URL("http", "host", 80, "/doc|search?q=green robots#over 6\"");
+        assertEquals("http", url.getProtocol());
+        assertEquals("host:80", url.getAuthority());
+        assertEquals("/doc|search", url.getPath());
+        assertEquals("q=green robots", url.getQuery());
+        assertEquals("over 6\"", url.getRef());
+        assertEquals("http://host:80/doc|search?q=green robots#over 6\"", url.toString());
+    }
+
+    public void testSchemeCaseIsCanonicalized() throws Exception {
+        URL url = new URL("HTTP://host/path");
+        assertEquals("http", url.getProtocol());
+    }
+
+    public void testEmptyAuthorityWithPath() throws Exception {
+        URL url = new URL("http:///path");
+        assertEquals("", url.getAuthority());
+        assertEquals("/path", url.getPath());
+    }
+
+    public void testEmptyAuthorityWithQuery() throws Exception {
+        URL url = new URL("http://?query");
+        assertEquals("", url.getAuthority());
+        assertEquals("", url.getPath());
+        assertEquals("query", url.getQuery());
+    }
+
+    public void testEmptyAuthorityWithFragment() throws Exception {
+        URL url = new URL("http://#fragment");
+        assertEquals("", url.getAuthority());
+        assertEquals("", url.getPath());
+        assertEquals("fragment", url.getRef());
+    }
+
+    public void testCombiningConstructorsMakeRelativePathsAbsolute() throws Exception {
+        assertEquals("/relative", new URL("http", "host", "relative").getPath());
+        assertEquals("/relative", new URL("http", "host", -1, "relative").getPath());
+        assertEquals("/relative", new URL("http", "host", -1, "relative", null).getPath());
+    }
+
+    public void testCombiningConstructorsDoNotMakeEmptyPathsAbsolute() throws Exception {
+        assertEquals("", new URL("http", "host", "").getPath());
+        assertEquals("", new URL("http", "host", -1, "").getPath());
+        assertEquals("", new URL("http", "host", -1, "", null).getPath());
+    }
+
     // Adding a new test? Consider adding an equivalent test to URITest.java
 }