1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package java.net;
28
29import java.io.IOException;
30import java.io.InputStream;
31import java.io.InvalidObjectException;
32import java.io.ObjectInputStream.GetField;
33import java.io.ObjectStreamException;
34import java.io.ObjectStreamField;
35import java.util.Collections;
36import java.util.HashSet;
37import java.util.Hashtable;
38import java.util.Set;
39import java.util.StringTokenizer;
40import sun.security.util.SecurityConstants;
41
42/**
43 * Class {@code URL} represents a Uniform Resource
44 * Locator, a pointer to a "resource" on the World
45 * Wide Web. A resource can be something as simple as a file or a
46 * directory, or it can be a reference to a more complicated object,
47 * such as a query to a database or to a search engine. More
48 * information on the types of URLs and their formats can be found at:
49 * <a href=
50 * "http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html">
51 * <i>Types of URL</i></a>
52 * <p>
53 * In general, a URL can be broken into several parts. Consider the
54 * following example:
55 * <blockquote><pre>
56 *     http://www.example.com/docs/resource1.html
57 * </pre></blockquote>
58 * <p>
59 * The URL above indicates that the protocol to use is
60 * {@code http} (HyperText Transfer Protocol) and that the
61 * information resides on a host machine named
62 * {@code www.example.com}. The information on that host
63 * machine is named {@code /docs/resource1.html}. The exact
64 * meaning of this name on the host machine is both protocol
65 * dependent and host dependent. The information normally resides in
66 * a file, but it could be generated on the fly. This component of
67 * the URL is called the <i>path</i> component.
68 * <p>
69 * A URL can optionally specify a "port", which is the
70 * port number to which the TCP connection is made on the remote host
71 * machine. If the port is not specified, the default port for
72 * the protocol is used instead. For example, the default port for
73 * {@code http} is {@code 80}. An alternative port could be
74 * specified as:
75 * <blockquote><pre>
76 *     http://www.example.com:1080/docs/resource1.html
77 * </pre></blockquote>
78 * <p>
79 * The syntax of {@code URL} is defined by  <a
80 * href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC&nbsp;2396: Uniform
81 * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a
82 * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC&nbsp;2732: Format for
83 * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format
84 * also supports scope_ids. The syntax and usage of scope_ids is described
85 * <a href="Inet6Address.html#scoped">here</a>.
86 * <p>
87 * A URL may have appended to it a "fragment", also known
88 * as a "ref" or a "reference". The fragment is indicated by the sharp
89 * sign character "#" followed by more characters. For example,
90 * <blockquote><pre>
91 *     http://java.sun.com/index.html#chapter1
92 * </pre></blockquote>
93 * <p>
94 * This fragment is not technically part of the URL. Rather, it
95 * indicates that after the specified resource is retrieved, the
96 * application is specifically interested in that part of the
97 * document that has the tag {@code chapter1} attached to it. The
98 * meaning of a tag is resource specific.
99 * <p>
100 * An application can also specify a "relative URL",
101 * which contains only enough information to reach the resource
102 * relative to another URL. Relative URLs are frequently used within
103 * HTML pages. For example, if the contents of the URL:
104 * <blockquote><pre>
105 *     http://java.sun.com/index.html
106 * </pre></blockquote>
107 * contained within it the relative URL:
108 * <blockquote><pre>
109 *     FAQ.html
110 * </pre></blockquote>
111 * it would be a shorthand for:
112 * <blockquote><pre>
113 *     http://java.sun.com/FAQ.html
114 * </pre></blockquote>
115 * <p>
116 * The relative URL need not specify all the components of a URL. If
117 * the protocol, host name, or port number is missing, the value is
118 * inherited from the fully specified URL. The file component must be
119 * specified. The optional fragment is not inherited.
120 * <p>
121 * The URL class does not itself encode or decode any URL components
122 * according to the escaping mechanism defined in RFC2396. It is the
123 * responsibility of the caller to encode any fields, which need to be
124 * escaped prior to calling URL, and also to decode any escaped fields,
125 * that are returned from URL. Furthermore, because URL has no knowledge
126 * of URL escaping, it does not recognise equivalence between the encoded
127 * or decoded form of the same URL. For example, the two URLs:<br>
128 * <pre>    http://foo.com/hello world/ and http://foo.com/hello%20world</pre>
129 * would be considered not equal to each other.
130 * <p>
131 * Note, the {@link java.net.URI} class does perform escaping of its
132 * component fields in certain circumstances. The recommended way
133 * to manage the encoding and decoding of URLs is to use {@link java.net.URI},
134 * and to convert between these two classes using {@link #toURI()} and
135 * {@link URI#toURL()}.
136 * <p>
137 * The {@link URLEncoder} and {@link URLDecoder} classes can also be
138 * used, but only for HTML form encoding, which is not the same
139 * as the encoding scheme defined in RFC2396.
140 *
141 * @author  James Gosling
142 * @since JDK1.0
143 */
144public final class URL implements java.io.Serializable {
145
146    // Android-changed: Custom built-in URLStreamHandlers for http, https.
147    // static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
148    private static final Set<String> BUILTIN_HANDLER_CLASS_NAMES = createBuiltinHandlerClassNames();
149    static final long serialVersionUID = -7627629688361524110L;
150
151    /**
152     * The property which specifies the package prefix list to be scanned
153     * for protocol handlers.  The value of this property (if any) should
154     * be a vertical bar delimited list of package names to search through
155     * for a protocol handler to load.  The policy of this class is that
156     * all protocol handlers will be in a class called <protocolname>.Handler,
157     * and each package in the list is examined in turn for a matching
158     * handler.  If none are found (or the property is not specified), the
159     * default package prefix, sun.net.www.protocol, is used.  The search
160     * proceeds from the first package in the list to the last and stops
161     * when a match is found.
162     */
163    private static final String protocolPathProp = "java.protocol.handler.pkgs";
164
165    /**
166     * The protocol to use (ftp, http, nntp, ... etc.) .
167     * @serial
168     */
169    private String protocol;
170
171    /**
172     * The host name to connect to.
173     * @serial
174     */
175    private String host;
176
177    /**
178     * The protocol port to connect to.
179     * @serial
180     */
181    private int port = -1;
182
183    /**
184     * The specified file name on that host. {@code file} is
185     * defined as {@code path[?query]}
186     * @serial
187     */
188    private String file;
189
190    /**
191     * The query part of this URL.
192     */
193    private transient String query;
194
195    /**
196     * The authority part of this URL.
197     * @serial
198     */
199    private String authority;
200
201    /**
202     * The path part of this URL.
203     */
204    private transient String path;
205
206    /**
207     * The userinfo part of this URL.
208     */
209    private transient String userInfo;
210
211    /**
212     * # reference.
213     * @serial
214     */
215    private String ref;
216
217    /**
218     * The host's IP address, used in equals and hashCode.
219     * Computed on demand. An uninitialized or unknown hostAddress is null.
220     */
221    transient InetAddress hostAddress;
222
223    /**
224     * The URLStreamHandler for this URL.
225     */
226    transient URLStreamHandler handler;
227
228    /* Our hash code.
229     * @serial
230     */
231    private int hashCode = -1;
232
233    private transient UrlDeserializedState tempState;
234
235    /**
236     * Creates a {@code URL} object from the specified
237     * {@code protocol}, {@code host}, {@code port}
238     * number, and {@code file}.<p>
239     *
240     * {@code host} can be expressed as a host name or a literal
241     * IP address. If IPv6 literal address is used, it should be
242     * enclosed in square brackets ({@code '['} and {@code ']'}), as
243     * specified by <a
244     * href="http://www.ietf.org/rfc/rfc2732.txt">RFC&nbsp;2732</a>;
245     * However, the literal IPv6 address format defined in <a
246     * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC&nbsp;2373: IP
247     * Version 6 Addressing Architecture</i></a> is also accepted.<p>
248     *
249     * Specifying a {@code port} number of {@code -1}
250     * indicates that the URL should use the default port for the
251     * protocol.<p>
252     *
253     * If this is the first URL object being created with the specified
254     * protocol, a <i>stream protocol handler</i> object, an instance of
255     * class {@code URLStreamHandler}, is created for that protocol:
256     * <ol>
257     * <li>If the application has previously set up an instance of
258     *     {@code URLStreamHandlerFactory} as the stream handler factory,
259     *     then the {@code createURLStreamHandler} method of that instance
260     *     is called with the protocol string as an argument to create the
261     *     stream protocol handler.
262     * <li>If no {@code URLStreamHandlerFactory} has yet been set up,
263     *     or if the factory's {@code createURLStreamHandler} method
264     *     returns {@code null}, then the constructor finds the
265     *     value of the system property:
266     *     <blockquote><pre>
267     *         java.protocol.handler.pkgs
268     *     </pre></blockquote>
269     *     If the value of that system property is not {@code null},
270     *     it is interpreted as a list of packages separated by a vertical
271     *     slash character '{@code |}'. The constructor tries to load
272     *     the class named:
273     *     <blockquote><pre>
274     *         &lt;<i>package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
275     *     </pre></blockquote>
276     *     where &lt;<i>package</i>&gt; is replaced by the name of the package
277     *     and &lt;<i>protocol</i>&gt; is replaced by the name of the protocol.
278     *     If this class does not exist, or if the class exists but it is not
279     *     a subclass of {@code URLStreamHandler}, then the next package
280     *     in the list is tried.
281     * <li>If the previous step fails to find a protocol handler, then the
282     *     constructor tries to load from a system default package.
283     *     <blockquote><pre>
284     *         &lt;<i>system default package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
285     *     </pre></blockquote>
286     *     If this class does not exist, or if the class exists but it is not a
287     *     subclass of {@code URLStreamHandler}, then a
288     *     {@code MalformedURLException} is thrown.
289     * </ol>
290     *
291     * <p>Protocol handlers for the following protocols are guaranteed
292     * to exist on the search path :-
293     * <blockquote><pre>
294     *     http, https, file, and jar
295     * </pre></blockquote>
296     * Protocol handlers for additional protocols may also be
297     * available.
298     *
299     * <p>No validation of the inputs is performed by this constructor.
300     *
301     * @param      protocol   the name of the protocol to use.
302     * @param      host       the name of the host.
303     * @param      port       the port number on the host.
304     * @param      file       the file on the host
305     * @exception  MalformedURLException  if an unknown protocol is specified.
306     * @see        java.lang.System#getProperty(java.lang.String)
307     * @see        java.net.URL#setURLStreamHandlerFactory(
308     *                  java.net.URLStreamHandlerFactory)
309     * @see        java.net.URLStreamHandler
310     * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
311     *                  java.lang.String)
312     */
313    public URL(String protocol, String host, int port, String file)
314        throws MalformedURLException
315    {
316        this(protocol, host, port, file, null);
317    }
318
319    /**
320     * Creates a URL from the specified {@code protocol}
321     * name, {@code host} name, and {@code file} name. The
322     * default port for the specified protocol is used.
323     * <p>
324     * This method is equivalent to calling the four-argument
325     * constructor with the arguments being {@code protocol},
326     * {@code host}, {@code -1}, and {@code file}.
327     *
328     * No validation of the inputs is performed by this constructor.
329     *
330     * @param      protocol   the name of the protocol to use.
331     * @param      host       the name of the host.
332     * @param      file       the file on the host.
333     * @exception  MalformedURLException  if an unknown protocol is specified.
334     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
335     *                  int, java.lang.String)
336     */
337    public URL(String protocol, String host, String file)
338            throws MalformedURLException {
339        this(protocol, host, -1, file);
340    }
341
342    /**
343     * Creates a {@code URL} object from the specified
344     * {@code protocol}, {@code host}, {@code port}
345     * number, {@code file}, and {@code handler}. Specifying
346     * a {@code port} number of {@code -1} indicates that
347     * the URL should use the default port for the protocol. Specifying
348     * a {@code handler} of {@code null} indicates that the URL
349     * should use a default stream handler for the protocol, as outlined
350     * for:
351     *     java.net.URL#URL(java.lang.String, java.lang.String, int,
352     *                      java.lang.String)
353     *
354     * <p>If the handler is not null and there is a security manager,
355     * the security manager's {@code checkPermission}
356     * method is called with a
357     * {@code NetPermission("specifyStreamHandler")} permission.
358     * This may result in a SecurityException.
359     *
360     * No validation of the inputs is performed by this constructor.
361     *
362     * @param      protocol   the name of the protocol to use.
363     * @param      host       the name of the host.
364     * @param      port       the port number on the host.
365     * @param      file       the file on the host
366     * @param      handler    the stream handler for the URL.
367     * @exception  MalformedURLException  if an unknown protocol is specified.
368     * @exception  SecurityException
369     *        if a security manager exists and its
370     *        {@code checkPermission} method doesn't allow
371     *        specifying a stream handler explicitly.
372     * @see        java.lang.System#getProperty(java.lang.String)
373     * @see        java.net.URL#setURLStreamHandlerFactory(
374     *                  java.net.URLStreamHandlerFactory)
375     * @see        java.net.URLStreamHandler
376     * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
377     *                  java.lang.String)
378     * @see        SecurityManager#checkPermission
379     * @see        java.net.NetPermission
380     */
381    public URL(String protocol, String host, int port, String file,
382               URLStreamHandler handler) throws MalformedURLException {
383        if (handler != null) {
384            SecurityManager sm = System.getSecurityManager();
385            if (sm != null) {
386                // check for permission to specify a handler
387                checkSpecifyHandler(sm);
388            }
389        }
390
391        protocol = protocol.toLowerCase();
392        this.protocol = protocol;
393        if (host != null) {
394
395            /**
396             * if host is a literal IPv6 address,
397             * we will make it conform to RFC 2732
398             */
399            if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
400                host = "["+host+"]";
401            }
402            this.host = host;
403
404            if (port < -1) {
405                throw new MalformedURLException("Invalid port number :" +
406                                                    port);
407            }
408            this.port = port;
409            authority = (port == -1) ? host : host + ":" + port;
410        }
411
412        // Android-changed: App compat. Prepend '/' if host is null / empty
413        // Parts parts = new Parts(file);
414        Parts parts = new Parts(file, host);
415        path = parts.getPath();
416        query = parts.getQuery();
417
418        if (query != null) {
419            this.file = path + "?" + query;
420        } else {
421            this.file = path;
422        }
423        ref = parts.getRef();
424
425        // Note: we don't do validation of the URL here. Too risky to change
426        // right now, but worth considering for future reference. -br
427        if (handler == null &&
428            (handler = getURLStreamHandler(protocol)) == null) {
429            throw new MalformedURLException("unknown protocol: " + protocol);
430        }
431        this.handler = handler;
432    }
433
434    /**
435     * Creates a {@code URL} object from the {@code String}
436     * representation.
437     * <p>
438     * This constructor is equivalent to a call to the two-argument
439     * constructor with a {@code null} first argument.
440     *
441     * @param      spec   the {@code String} to parse as a URL.
442     * @exception  MalformedURLException  if no protocol is specified, or an
443     *               unknown protocol is found, or {@code spec} is {@code null}.
444     * @see        java.net.URL#URL(java.net.URL, java.lang.String)
445     */
446    public URL(String spec) throws MalformedURLException {
447        this(null, spec);
448    }
449
450    /**
451     * Creates a URL by parsing the given spec within a specified context.
452     *
453     * The new URL is created from the given context URL and the spec
454     * argument as described in
455     * RFC2396 &quot;Uniform Resource Identifiers : Generic * Syntax&quot; :
456     * <blockquote><pre>
457     *          &lt;scheme&gt;://&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
458     * </pre></blockquote>
459     * The reference is parsed into the scheme, authority, path, query and
460     * fragment parts. If the path component is empty and the scheme,
461     * authority, and query components are undefined, then the new URL is a
462     * reference to the current document. Otherwise, the fragment and query
463     * parts present in the spec are used in the new URL.
464     * <p>
465     * If the scheme component is defined in the given spec and does not match
466     * the scheme of the context, then the new URL is created as an absolute
467     * URL based on the spec alone. Otherwise the scheme component is inherited
468     * from the context URL.
469     * <p>
470     * If the authority component is present in the spec then the spec is
471     * treated as absolute and the spec authority and path will replace the
472     * context authority and path. If the authority component is absent in the
473     * spec then the authority of the new URL will be inherited from the
474     * context.
475     * <p>
476     * If the spec's path component begins with a slash character
477     * &quot;/&quot; then the
478     * path is treated as absolute and the spec path replaces the context path.
479     * <p>
480     * Otherwise, the path is treated as a relative path and is appended to the
481     * context path, as described in RFC2396. Also, in this case,
482     * the path is canonicalized through the removal of directory
483     * changes made by occurrences of &quot;..&quot; and &quot;.&quot;.
484     * <p>
485     * For a more detailed description of URL parsing, refer to RFC2396.
486     *
487     * @param      context   the context in which to parse the specification.
488     * @param      spec      the {@code String} to parse as a URL.
489     * @exception  MalformedURLException  if no protocol is specified, or an
490     *               unknown protocol is found, or {@code spec} is {@code null}.
491     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
492     *                  int, java.lang.String)
493     * @see        java.net.URLStreamHandler
494     * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
495     *                  java.lang.String, int, int)
496     */
497    public URL(URL context, String spec) throws MalformedURLException {
498        this(context, spec, null);
499    }
500
501    /**
502     * Creates a URL by parsing the given spec with the specified handler
503     * within a specified context. If the handler is null, the parsing
504     * occurs as with the two argument constructor.
505     *
506     * @param      context   the context in which to parse the specification.
507     * @param      spec      the {@code String} to parse as a URL.
508     * @param      handler   the stream handler for the URL.
509     * @exception  MalformedURLException  if no protocol is specified, or an
510     *               unknown protocol is found, or {@code spec} is {@code null}.
511     * @exception  SecurityException
512     *        if a security manager exists and its
513     *        {@code checkPermission} method doesn't allow
514     *        specifying a stream handler.
515     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
516     *                  int, java.lang.String)
517     * @see        java.net.URLStreamHandler
518     * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
519     *                  java.lang.String, int, int)
520     */
521    public URL(URL context, String spec, URLStreamHandler handler)
522        throws MalformedURLException
523    {
524        String original = spec;
525        int i, limit, c;
526        int start = 0;
527        String newProtocol = null;
528        boolean aRef=false;
529        boolean isRelative = false;
530
531        // Check for permission to specify a handler
532        if (handler != null) {
533            SecurityManager sm = System.getSecurityManager();
534            if (sm != null) {
535                checkSpecifyHandler(sm);
536            }
537        }
538
539        try {
540            limit = spec.length();
541            while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
542                limit--;        //eliminate trailing whitespace
543            }
544            while ((start < limit) && (spec.charAt(start) <= ' ')) {
545                start++;        // eliminate leading whitespace
546            }
547
548            if (spec.regionMatches(true, start, "url:", 0, 4)) {
549                start += 4;
550            }
551            if (start < spec.length() && spec.charAt(start) == '#') {
552                /* we're assuming this is a ref relative to the context URL.
553                 * This means protocols cannot start w/ '#', but we must parse
554                 * ref URL's like: "hello:there" w/ a ':' in them.
555                 */
556                aRef=true;
557            }
558            for (i = start ; !aRef && (i < limit) &&
559                     ((c = spec.charAt(i)) != '/') ; i++) {
560                if (c == ':') {
561
562                    String s = spec.substring(start, i).toLowerCase();
563                    if (isValidProtocol(s)) {
564                        newProtocol = s;
565                        start = i + 1;
566                    }
567                    break;
568                }
569            }
570
571            // Only use our context if the protocols match.
572            protocol = newProtocol;
573            if ((context != null) && ((newProtocol == null) ||
574                            newProtocol.equalsIgnoreCase(context.protocol))) {
575                // inherit the protocol handler from the context
576                // if not specified to the constructor
577                if (handler == null) {
578                    handler = context.handler;
579                }
580
581                // If the context is a hierarchical URL scheme and the spec
582                // contains a matching scheme then maintain backwards
583                // compatibility and treat it as if the spec didn't contain
584                // the scheme; see 5.2.3 of RFC2396
585                if (context.path != null && context.path.startsWith("/"))
586                    newProtocol = null;
587
588                if (newProtocol == null) {
589                    protocol = context.protocol;
590                    authority = context.authority;
591                    userInfo = context.userInfo;
592                    host = context.host;
593                    port = context.port;
594                    file = context.file;
595                    path = context.path;
596                    isRelative = true;
597                }
598            }
599
600            if (protocol == null) {
601                throw new MalformedURLException("no protocol: "+original);
602            }
603
604            // Get the protocol handler if not specified or the protocol
605            // of the context could not be used
606            if (handler == null &&
607                (handler = getURLStreamHandler(protocol)) == null) {
608                throw new MalformedURLException("unknown protocol: "+protocol);
609            }
610
611            this.handler = handler;
612
613            i = spec.indexOf('#', start);
614            if (i >= 0) {
615                ref = spec.substring(i + 1, limit);
616                limit = i;
617            }
618
619            /*
620             * Handle special case inheritance of query and fragment
621             * implied by RFC2396 section 5.2.2.
622             */
623            if (isRelative && start == limit) {
624                query = context.query;
625                if (ref == null) {
626                    ref = context.ref;
627                }
628            }
629
630            handler.parseURL(this, spec, start, limit);
631
632        } catch(MalformedURLException e) {
633            throw e;
634        } catch(Exception e) {
635            MalformedURLException exception = new MalformedURLException(e.getMessage());
636            exception.initCause(e);
637            throw exception;
638        }
639    }
640
641    /*
642     * Returns true if specified string is a valid protocol name.
643     */
644    private boolean isValidProtocol(String protocol) {
645        int len = protocol.length();
646        if (len < 1)
647            return false;
648        char c = protocol.charAt(0);
649        if (!Character.isLetter(c))
650            return false;
651        for (int i = 1; i < len; i++) {
652            c = protocol.charAt(i);
653            if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
654                c != '-') {
655                return false;
656            }
657        }
658        return true;
659    }
660
661    /*
662     * Checks for permission to specify a stream handler.
663     */
664    private void checkSpecifyHandler(SecurityManager sm) {
665        sm.checkPermission(SecurityConstants.SPECIFY_HANDLER_PERMISSION);
666    }
667
668    /**
669     * Sets the fields of the URL. This is not a public method so that
670     * only URLStreamHandlers can modify URL fields. URLs are
671     * otherwise constant.
672     *
673     * @param protocol the name of the protocol to use
674     * @param host the name of the host
675       @param port the port number on the host
676     * @param file the file on the host
677     * @param ref the internal reference in the URL
678     */
679    void set(String protocol, String host, int port,
680             String file, String ref) {
681        synchronized (this) {
682            this.protocol = protocol;
683            this.host = host;
684            authority = port == -1 ? host : host + ":" + port;
685            this.port = port;
686            this.file = file;
687            this.ref = ref;
688            /* This is very important. We must recompute this after the
689             * URL has been changed. */
690            hashCode = -1;
691            hostAddress = null;
692            int q = file.lastIndexOf('?');
693            if (q != -1) {
694                query = file.substring(q+1);
695                path = file.substring(0, q);
696            } else
697                path = file;
698        }
699    }
700
701    /**
702     * Sets the specified 8 fields of the URL. This is not a public method so
703     * that only URLStreamHandlers can modify URL fields. URLs are otherwise
704     * constant.
705     *
706     * @param protocol the name of the protocol to use
707     * @param host the name of the host
708     * @param port the port number on the host
709     * @param authority the authority part for the url
710     * @param userInfo the username and password
711     * @param path the file on the host
712     * @param ref the internal reference in the URL
713     * @param query the query part of this URL
714     * @since 1.3
715     */
716    void set(String protocol, String host, int port,
717             String authority, String userInfo, String path,
718             String query, String ref) {
719        synchronized (this) {
720            this.protocol = protocol;
721            this.host = host;
722            this.port = port;
723            // Android-changed: App compat. Only include query part if it's nonempty.
724            // this.file = query == null ? path : path + "?" + query;
725            this.file = (query == null || query.isEmpty()) ? path : path + "?" + query;
726            this.userInfo = userInfo;
727            this.path = path;
728            this.ref = ref;
729            /* This is very important. We must recompute this after the
730             * URL has been changed. */
731            hashCode = -1;
732            hostAddress = null;
733            this.query = query;
734            this.authority = authority;
735        }
736    }
737
738    /**
739     * Gets the query part of this {@code URL}.
740     *
741     * @return  the query part of this {@code URL},
742     * or <CODE>null</CODE> if one does not exist
743     * @since 1.3
744     */
745    public String getQuery() {
746        return query;
747    }
748
749    /**
750     * Gets the path part of this {@code URL}.
751     *
752     * @return  the path part of this {@code URL}, or an
753     * empty string if one does not exist
754     * @since 1.3
755     */
756    public String getPath() {
757        return path;
758    }
759
760    /**
761     * Gets the userInfo part of this {@code URL}.
762     *
763     * @return  the userInfo part of this {@code URL}, or
764     * <CODE>null</CODE> if one does not exist
765     * @since 1.3
766     */
767    public String getUserInfo() {
768        return userInfo;
769    }
770
771    /**
772     * Gets the authority part of this {@code URL}.
773     *
774     * @return  the authority part of this {@code URL}
775     * @since 1.3
776     */
777    public String getAuthority() {
778        return authority;
779    }
780
781    /**
782     * Gets the port number of this {@code URL}.
783     *
784     * @return  the port number, or -1 if the port is not set
785     */
786    public int getPort() {
787        return port;
788    }
789
790    /**
791     * Gets the default port number of the protocol associated
792     * with this {@code URL}. If the URL scheme or the URLStreamHandler
793     * for the URL do not define a default port number,
794     * then -1 is returned.
795     *
796     * @return  the port number
797     * @since 1.4
798     */
799    public int getDefaultPort() {
800        return handler.getDefaultPort();
801    }
802
803    /**
804     * Gets the protocol name of this {@code URL}.
805     *
806     * @return  the protocol of this {@code URL}.
807     */
808    public String getProtocol() {
809        return protocol;
810    }
811
812    /**
813     * Gets the host name of this {@code URL}, if applicable.
814     * The format of the host conforms to RFC 2732, i.e. for a
815     * literal IPv6 address, this method will return the IPv6 address
816     * enclosed in square brackets ({@code '['} and {@code ']'}).
817     *
818     * @return  the host name of this {@code URL}.
819     */
820    public String getHost() {
821        return host;
822    }
823
824    /**
825     * Gets the file name of this {@code URL}.
826     * The returned file portion will be
827     * the same as <CODE>getPath()</CODE>, plus the concatenation of
828     * the value of <CODE>getQuery()</CODE>, if any. If there is
829     * no query portion, this method and <CODE>getPath()</CODE> will
830     * return identical results.
831     *
832     * @return  the file name of this {@code URL},
833     * or an empty string if one does not exist
834     */
835    public String getFile() {
836        return file;
837    }
838
839    /**
840     * Gets the anchor (also known as the "reference") of this
841     * {@code URL}.
842     *
843     * @return  the anchor (also known as the "reference") of this
844     *          {@code URL}, or <CODE>null</CODE> if one does not exist
845     */
846    public String getRef() {
847        return ref;
848    }
849
850    // Android-changed: Don't let URL.equals() attempt to resolve host names.
851    /**
852     * Compares this URL for equality with another object.<p>
853     *
854     * If the given object is not a URL then this method immediately returns
855     * {@code false}.<p>
856     *
857     * Two URL objects are equal if they have the same protocol, reference
858     * equivalent hosts, have the same port number on the host, and the same
859     * file and fragment of the file.<p>
860     *
861     * Returns true if this URL equals {@code o}. URLs are equal if they have
862     * the same protocol, host, port, file, and reference.
863     *
864     * <h3>Network I/O Warning</h3>
865     * <p>Some implementations of URL.equals() resolve host names over the
866     * network. This is problematic:
867     * <ul>
868     * <li><strong>The network may be slow.</strong> Many classes, including
869     * core collections like {@link java.util.Map Map} and {@link java.util.Set
870     * Set} expect that {@code equals} and {@code hashCode} will return quickly.
871     * By violating this assumption, this method posed potential performance
872     * problems.
873     * <li><strong>Equal IP addresses do not imply equal content.</strong>
874     * Virtual hosting permits unrelated sites to share an IP address. This
875     * method could report two otherwise unrelated URLs to be equal because
876     * they're hosted on the same server.</li>
877     * <li><strong>The network may not be available.</strong> Two URLs could be
878     * equal when a network is available and unequal otherwise.</li>
879     * <li><strong>The network may change.</strong> The IP address for a given
880     * host name varies by network and over time. This is problematic for mobile
881     * devices. Two URLs could be equal on some networks and unequal on
882     * others.</li>
883     * </ul>
884     * <p>This problem is fixed in Android 4.0 (Ice Cream Sandwich). In that
885     * release, URLs are only equal if their host names are equal (ignoring
886     * case).
887     *
888     * @param   obj   the URL to compare against.
889     * @return  {@code true} if the objects are the same;
890     *          {@code false} otherwise.
891     */
892    public boolean equals(Object obj) {
893        if (!(obj instanceof URL))
894            return false;
895        URL u2 = (URL)obj;
896
897        return handler.equals(this, u2);
898    }
899
900    /**
901     * Creates an integer suitable for hash table indexing.<p>
902     *
903     * The hash code is based upon all the URL components relevant for URL
904     * comparison. As such, this operation is a blocking operation.<p>
905     *
906     * @return  a hash code for this {@code URL}.
907     */
908    public synchronized int hashCode() {
909        if (hashCode != -1)
910            return hashCode;
911
912        hashCode = handler.hashCode(this);
913        return hashCode;
914    }
915
916    /**
917     * Compares two URLs, excluding the fragment component.<p>
918     *
919     * Returns {@code true} if this {@code URL} and the
920     * {@code other} argument are equal without taking the
921     * fragment component into consideration.
922     *
923     * @param   other   the {@code URL} to compare against.
924     * @return  {@code true} if they reference the same remote object;
925     *          {@code false} otherwise.
926     */
927    public boolean sameFile(URL other) {
928        return handler.sameFile(this, other);
929    }
930
931    /**
932     * Constructs a string representation of this {@code URL}. The
933     * string is created by calling the {@code toExternalForm}
934     * method of the stream protocol handler for this object.
935     *
936     * @return  a string representation of this object.
937     * @see     java.net.URL#URL(java.lang.String, java.lang.String, int,
938     *                  java.lang.String)
939     * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
940     */
941    public String toString() {
942        return toExternalForm();
943    }
944
945    /**
946     * Constructs a string representation of this {@code URL}. The
947     * string is created by calling the {@code toExternalForm}
948     * method of the stream protocol handler for this object.
949     *
950     * @return  a string representation of this object.
951     * @see     java.net.URL#URL(java.lang.String, java.lang.String,
952     *                  int, java.lang.String)
953     * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
954     */
955    public String toExternalForm() {
956        return handler.toExternalForm(this);
957    }
958
959    /**
960     * Returns a {@link java.net.URI} equivalent to this URL.
961     * This method functions in the same way as {@code new URI (this.toString())}.
962     * <p>Note, any URL instance that complies with RFC 2396 can be converted
963     * to a URI. However, some URLs that are not strictly in compliance
964     * can not be converted to a URI.
965     *
966     * @exception URISyntaxException if this URL is not formatted strictly according to
967     *            to RFC2396 and cannot be converted to a URI.
968     *
969     * @return    a URI instance equivalent to this URL.
970     * @since 1.5
971     */
972    public URI toURI() throws URISyntaxException {
973        return new URI (toString());
974    }
975
976    /**
977     * Returns a {@link java.net.URLConnection URLConnection} instance that
978     * represents a connection to the remote object referred to by the
979     * {@code URL}.
980     *
981     * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
982     * created every time when invoking the
983     * {@linkplain java.net.URLStreamHandler#openConnection(URL)
984     * URLStreamHandler.openConnection(URL)} method of the protocol handler for
985     * this URL.</P>
986     *
987     * <P>It should be noted that a URLConnection instance does not establish
988     * the actual network connection on creation. This will happen only when
989     * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P>
990     *
991     * <P>If for the URL's protocol (such as HTTP or JAR), there
992     * exists a public, specialized URLConnection subclass belonging
993     * to one of the following packages or one of their subpackages:
994     * java.lang, java.io, java.util, java.net, the connection
995     * returned will be of that subclass. For example, for HTTP an
996     * HttpURLConnection will be returned, and for JAR a
997     * JarURLConnection will be returned.</P>
998     *
999     * @return     a {@link java.net.URLConnection URLConnection} linking
1000     *             to the URL.
1001     * @exception  IOException  if an I/O exception occurs.
1002     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
1003     *             int, java.lang.String)
1004     */
1005    public URLConnection openConnection() throws java.io.IOException {
1006        return handler.openConnection(this);
1007    }
1008
1009    /**
1010     * Same as {@link #openConnection()}, except that the connection will be
1011     * made through the specified proxy; Protocol handlers that do not
1012     * support proxing will ignore the proxy parameter and make a
1013     * normal connection.
1014     *
1015     * Invoking this method preempts the system's default ProxySelector
1016     * settings.
1017     *
1018     * @param      proxy the Proxy through which this connection
1019     *             will be made. If direct connection is desired,
1020     *             Proxy.NO_PROXY should be specified.
1021     * @return     a {@code URLConnection} to the URL.
1022     * @exception  IOException  if an I/O exception occurs.
1023     * @exception  SecurityException if a security manager is present
1024     *             and the caller doesn't have permission to connect
1025     *             to the proxy.
1026     * @exception  IllegalArgumentException will be thrown if proxy is null,
1027     *             or proxy has the wrong type
1028     * @exception  UnsupportedOperationException if the subclass that
1029     *             implements the protocol handler doesn't support
1030     *             this method.
1031     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
1032     *             int, java.lang.String)
1033     * @see        java.net.URLConnection
1034     * @see        java.net.URLStreamHandler#openConnection(java.net.URL,
1035     *             java.net.Proxy)
1036     * @since      1.5
1037     */
1038    public URLConnection openConnection(Proxy proxy)
1039        throws java.io.IOException {
1040        if (proxy == null) {
1041            throw new IllegalArgumentException("proxy can not be null");
1042        }
1043
1044        // Create a copy of Proxy as a security measure
1045        Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
1046        SecurityManager sm = System.getSecurityManager();
1047        if (p.type() != Proxy.Type.DIRECT && sm != null) {
1048            InetSocketAddress epoint = (InetSocketAddress) p.address();
1049            if (epoint.isUnresolved())
1050                sm.checkConnect(epoint.getHostName(), epoint.getPort());
1051            else
1052                sm.checkConnect(epoint.getAddress().getHostAddress(),
1053                                epoint.getPort());
1054        }
1055        return handler.openConnection(this, p);
1056    }
1057
1058    /**
1059     * Opens a connection to this {@code URL} and returns an
1060     * {@code InputStream} for reading from that connection. This
1061     * method is a shorthand for:
1062     * <blockquote><pre>
1063     *     openConnection().getInputStream()
1064     * </pre></blockquote>
1065     *
1066     * @return     an input stream for reading from the URL connection.
1067     * @exception  IOException  if an I/O exception occurs.
1068     * @see        java.net.URL#openConnection()
1069     * @see        java.net.URLConnection#getInputStream()
1070     */
1071    public final InputStream openStream() throws java.io.IOException {
1072        return openConnection().getInputStream();
1073    }
1074
1075    /**
1076     * Gets the contents of this URL. This method is a shorthand for:
1077     * <blockquote><pre>
1078     *     openConnection().getContent()
1079     * </pre></blockquote>
1080     *
1081     * @return     the contents of this URL.
1082     * @exception  IOException  if an I/O exception occurs.
1083     * @see        java.net.URLConnection#getContent()
1084     */
1085    public final Object getContent() throws java.io.IOException {
1086        return openConnection().getContent();
1087    }
1088
1089    /**
1090     * Gets the contents of this URL. This method is a shorthand for:
1091     * <blockquote><pre>
1092     *     openConnection().getContent(Class[])
1093     * </pre></blockquote>
1094     *
1095     * @param classes an array of Java types
1096     * @return     the content object of this URL that is the first match of
1097     *               the types specified in the classes array.
1098     *               null if none of the requested types are supported.
1099     * @exception  IOException  if an I/O exception occurs.
1100     * @see        java.net.URLConnection#getContent(Class[])
1101     * @since 1.3
1102     */
1103    public final Object getContent(Class[] classes)
1104    throws java.io.IOException {
1105        return openConnection().getContent(classes);
1106    }
1107
1108    /**
1109     * The URLStreamHandler factory.
1110     */
1111    static URLStreamHandlerFactory factory;
1112
1113    /**
1114     * Sets an application's {@code URLStreamHandlerFactory}.
1115     * This method can be called at most once in a given Java Virtual
1116     * Machine.
1117     *
1118     *<p> The {@code URLStreamHandlerFactory} instance is used to
1119     *construct a stream protocol handler from a protocol name.
1120     *
1121     * <p> If there is a security manager, this method first calls
1122     * the security manager's {@code checkSetFactory} method
1123     * to ensure the operation is allowed.
1124     * This could result in a SecurityException.
1125     *
1126     * @param      fac   the desired factory.
1127     * @exception  Error  if the application has already set a factory.
1128     * @exception  SecurityException  if a security manager exists and its
1129     *             {@code checkSetFactory} method doesn't allow
1130     *             the operation.
1131     * @see        java.net.URL#URL(java.lang.String, java.lang.String,
1132     *             int, java.lang.String)
1133     * @see        java.net.URLStreamHandlerFactory
1134     * @see        SecurityManager#checkSetFactory
1135     */
1136    public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
1137        synchronized (streamHandlerLock) {
1138            if (factory != null) {
1139                throw new Error("factory already defined");
1140            }
1141            SecurityManager security = System.getSecurityManager();
1142            if (security != null) {
1143                security.checkSetFactory();
1144            }
1145            handlers.clear();
1146            factory = fac;
1147        }
1148    }
1149
1150    /**
1151     * A table of protocol handlers.
1152     */
1153    static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
1154    private static Object streamHandlerLock = new Object();
1155
1156    /**
1157     * Returns the Stream Handler.
1158     * @param protocol the protocol to use
1159     */
1160    static URLStreamHandler getURLStreamHandler(String protocol) {
1161
1162        URLStreamHandler handler = handlers.get(protocol);
1163        if (handler == null) {
1164
1165            boolean checkedWithFactory = false;
1166
1167            // Use the factory (if any)
1168            if (factory != null) {
1169                handler = factory.createURLStreamHandler(protocol);
1170                checkedWithFactory = true;
1171            }
1172
1173            // Try java protocol handler
1174            if (handler == null) {
1175                // Android-changed: Android doesn't need AccessController.
1176                // Remove unnecessary use of reflection for sun classes
1177                /*
1178                packagePrefixList
1179                    = java.security.AccessController.doPrivileged(
1180                    new sun.security.action.GetPropertyAction(
1181                        protocolPathProp,""));
1182                if (packagePrefixList != "") {
1183                    packagePrefixList += "|";
1184                }
1185
1186                // REMIND: decide whether to allow the "null" class prefix
1187                // or not.
1188                packagePrefixList += "sun.net.www.protocol";
1189                 */
1190                final String packagePrefixList = System.getProperty(protocolPathProp,"");
1191
1192                StringTokenizer packagePrefixIter =
1193                    new StringTokenizer(packagePrefixList, "|");
1194
1195                while (handler == null &&
1196                       packagePrefixIter.hasMoreTokens()) {
1197
1198                    String packagePrefix =
1199                      packagePrefixIter.nextToken().trim();
1200                    try {
1201                        String clsName = packagePrefix + "." + protocol +
1202                          ".Handler";
1203                        Class<?> cls = null;
1204                        try {
1205                            ClassLoader cl = ClassLoader.getSystemClassLoader();
1206                            // BEGIN Android-changed: Fall back to thread's contextClassLoader.
1207                            // http://b/25897689
1208                            cls = Class.forName(clsName, true, cl);
1209                        } catch (ClassNotFoundException e) {
1210                            ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
1211                            if (contextLoader != null) {
1212                                cls = Class.forName(clsName, true, contextLoader);
1213                            }
1214                            // END Android-changed: Fall back to thread's contextClassLoader.
1215                        }
1216                        if (cls != null) {
1217                            handler  =
1218                              (URLStreamHandler)cls.newInstance();
1219                        }
1220                    } catch (ReflectiveOperationException ignored) {
1221                    }
1222                }
1223            }
1224
1225            // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
1226            // Fallback to built-in stream handler.
1227            if (handler == null) {
1228                try {
1229                    handler = createBuiltinHandler(protocol);
1230                } catch (Exception e) {
1231                    throw new AssertionError(e);
1232                }
1233            }
1234            // END Android-added: Custom built-in URLStreamHandlers for http, https.
1235
1236            synchronized (streamHandlerLock) {
1237
1238                URLStreamHandler handler2 = null;
1239
1240                // Check again with hashtable just in case another
1241                // thread created a handler since we last checked
1242                handler2 = handlers.get(protocol);
1243
1244                if (handler2 != null) {
1245                    return handler2;
1246                }
1247
1248                // Check with factory if another thread set a
1249                // factory since our last check
1250                if (!checkedWithFactory && factory != null) {
1251                    handler2 = factory.createURLStreamHandler(protocol);
1252                }
1253
1254                if (handler2 != null) {
1255                    // The handler from the factory must be given more
1256                    // importance. Discard the default handler that
1257                    // this thread created.
1258                    handler = handler2;
1259                }
1260
1261                // Insert this handler into the hashtable
1262                if (handler != null) {
1263                    handlers.put(protocol, handler);
1264                }
1265
1266            }
1267        }
1268
1269        return handler;
1270
1271    }
1272
1273    // BEGIN Android-added: Custom built-in URLStreamHandlers for http, https.
1274    /**
1275     * Returns an instance of the built-in handler for the given protocol, or null if none exists.
1276     */
1277    private static URLStreamHandler createBuiltinHandler(String protocol)
1278            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
1279        URLStreamHandler handler = null;
1280        if (protocol.equals("file")) {
1281            handler = new sun.net.www.protocol.file.Handler();
1282        } else if (protocol.equals("ftp")) {
1283            handler = new sun.net.www.protocol.ftp.Handler();
1284        } else if (protocol.equals("jar")) {
1285            handler = new sun.net.www.protocol.jar.Handler();
1286        } else if (protocol.equals("http")) {
1287            handler = (URLStreamHandler)Class.
1288                    forName("com.android.okhttp.HttpHandler").newInstance();
1289        } else if (protocol.equals("https")) {
1290            handler = (URLStreamHandler)Class.
1291                    forName("com.android.okhttp.HttpsHandler").newInstance();
1292        }
1293        return handler;
1294    }
1295
1296    /** Names of implementation classes returned by {@link #createBuiltinHandler(String)}. */
1297    private static Set<String> createBuiltinHandlerClassNames() {
1298        Set<String> result = new HashSet<>();
1299        // Refer to class names rather than classes to avoid needlessly triggering <clinit>.
1300        result.add("sun.net.www.protocol.file.Handler");
1301        result.add("sun.net.www.protocol.ftp.Handler");
1302        result.add("sun.net.www.protocol.jar.Handler");
1303        result.add("com.android.okhttp.HttpHandler");
1304        result.add("com.android.okhttp.HttpsHandler");
1305        return Collections.unmodifiableSet(result);
1306    }
1307    // END Android-added: Custom built-in URLStreamHandlers for http, https.
1308
1309    /**
1310     * @serialField    protocol String
1311     *
1312     * @serialField    host String
1313     *
1314     * @serialField    port int
1315     *
1316     * @serialField    authority String
1317     *
1318     * @serialField    file String
1319     *
1320     * @serialField    ref String
1321     *
1322     * @serialField    hashCode int
1323     *
1324     */
1325    private static final ObjectStreamField[] serialPersistentFields = {
1326        new ObjectStreamField("protocol", String.class),
1327        new ObjectStreamField("host", String.class),
1328        new ObjectStreamField("port", int.class),
1329        new ObjectStreamField("authority", String.class),
1330        new ObjectStreamField("file", String.class),
1331        new ObjectStreamField("ref", String.class),
1332    // Android-changed: App compat: hashCode should not be serialized.
1333    //    new ObjectStreamField("hashCode", int.class), };
1334    };
1335
1336    /**
1337     * WriteObject is called to save the state of the URL to an
1338     * ObjectOutputStream. The handler is not saved since it is
1339     * specific to this system.
1340     *
1341     * @serialData the default write object value. When read back in,
1342     * the reader must ensure that calling getURLStreamHandler with
1343     * the protocol variable returns a valid URLStreamHandler and
1344     * throw an IOException if it does not.
1345     */
1346    private synchronized void writeObject(java.io.ObjectOutputStream s)
1347        throws IOException
1348    {
1349        s.defaultWriteObject(); // write the fields
1350    }
1351
1352    /**
1353     * readObject is called to restore the state of the URL from the
1354     * stream.  It reads the components of the URL and finds the local
1355     * stream handler.
1356     */
1357    private synchronized void readObject(java.io.ObjectInputStream s)
1358            throws IOException, ClassNotFoundException {
1359        GetField gf = s.readFields();
1360        String protocol = (String)gf.get("protocol", null);
1361        if (getURLStreamHandler(protocol) == null) {
1362            throw new IOException("unknown protocol: " + protocol);
1363        }
1364        String host = (String)gf.get("host", null);
1365        int port = gf.get("port", -1);
1366        String authority = (String)gf.get("authority", null);
1367        String file = (String)gf.get("file", null);
1368        String ref = (String)gf.get("ref", null);
1369        // Android-changed: App compat: hashCode should not be serialized.
1370        // int hashCode = gf.get("hashCode", -1);
1371        final int hashCode = -1;
1372        if (authority == null
1373                && ((host != null && host.length() > 0) || port != -1)) {
1374            if (host == null)
1375                host = "";
1376            authority = (port == -1) ? host : host + ":" + port;
1377        }
1378        tempState = new UrlDeserializedState(protocol, host, port, authority,
1379               file, ref, hashCode);
1380    }
1381
1382    /**
1383     * Replaces the de-serialized object with an URL object.
1384     *
1385     * @return a newly created object from the deserialzed state.
1386     *
1387     * @throws ObjectStreamException if a new object replacing this
1388     * object could not be created
1389     */
1390
1391   private Object readResolve() throws ObjectStreamException {
1392
1393        URLStreamHandler handler = null;
1394        // already been checked in readObject
1395        handler = getURLStreamHandler(tempState.getProtocol());
1396
1397        URL replacementURL = null;
1398        if (isBuiltinStreamHandler(handler.getClass().getName())) {
1399            replacementURL = fabricateNewURL();
1400        } else {
1401            replacementURL = setDeserializedFields(handler);
1402        }
1403        return replacementURL;
1404    }
1405
1406    private URL setDeserializedFields(URLStreamHandler handler) {
1407        URL replacementURL;
1408        String userInfo = null;
1409        String protocol = tempState.getProtocol();
1410        String host = tempState.getHost();
1411        int port = tempState.getPort();
1412        String authority = tempState.getAuthority();
1413        String file = tempState.getFile();
1414        String ref = tempState.getRef();
1415        int hashCode = tempState.getHashCode();
1416
1417
1418        // Construct authority part
1419        if (authority == null
1420            && ((host != null && host.length() > 0) || port != -1)) {
1421            if (host == null)
1422                host = "";
1423            authority = (port == -1) ? host : host + ":" + port;
1424
1425            // Handle hosts with userInfo in them
1426            int at = host.lastIndexOf('@');
1427            if (at != -1) {
1428                userInfo = host.substring(0, at);
1429                host = host.substring(at+1);
1430            }
1431        } else if (authority != null) {
1432            // Construct user info part
1433            int ind = authority.indexOf('@');
1434            if (ind != -1)
1435                userInfo = authority.substring(0, ind);
1436        }
1437
1438        // Construct path and query part
1439        String path = null;
1440        String query = null;
1441        if (file != null) {
1442            // Fix: only do this if hierarchical?
1443            int q = file.lastIndexOf('?');
1444            if (q != -1) {
1445                query = file.substring(q+1);
1446                path = file.substring(0, q);
1447            } else
1448                path = file;
1449        }
1450
1451        // Set the object fields.
1452        this.protocol = protocol;
1453        this.host = host;
1454        this.port = port;
1455        this.file = file;
1456        this.authority = authority;
1457        this.ref = ref;
1458        this.hashCode = hashCode;
1459        this.handler = handler;
1460        this.query = query;
1461        this.path = path;
1462        this.userInfo = userInfo;
1463        replacementURL = this;
1464        return replacementURL;
1465    }
1466
1467    private URL fabricateNewURL()
1468                throws InvalidObjectException {
1469        // create URL string from deserialized object
1470        URL replacementURL = null;
1471        String urlString = tempState.reconstituteUrlString();
1472
1473        try {
1474            replacementURL = new URL(urlString);
1475        } catch (MalformedURLException mEx) {
1476            resetState();
1477            InvalidObjectException invoEx = new InvalidObjectException(
1478                    "Malformed URL: " + urlString);
1479            invoEx.initCause(mEx);
1480            throw invoEx;
1481        }
1482        replacementURL.setSerializedHashCode(tempState.getHashCode());
1483        resetState();
1484        return replacementURL;
1485    }
1486
1487    private boolean isBuiltinStreamHandler(String handlerClassName) {
1488        // Android-changed: Some built-in handlers (eg. HttpHandler) are not in sun.net.www.protocol.
1489        // return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX));
1490        return BUILTIN_HANDLER_CLASS_NAMES.contains(handlerClassName);
1491    }
1492
1493    private void resetState() {
1494        this.protocol = null;
1495        this.host = null;
1496        this.port = -1;
1497        this.file = null;
1498        this.authority = null;
1499        this.ref = null;
1500        this.hashCode = -1;
1501        this.handler = null;
1502        this.query = null;
1503        this.path = null;
1504        this.userInfo = null;
1505        this.tempState = null;
1506    }
1507
1508    private void setSerializedHashCode(int hc) {
1509        this.hashCode = hc;
1510    }
1511}
1512
1513class Parts {
1514    String path, query, ref;
1515
1516    // Android-changed: App compat. Prepend '/' if host is null / empty.
1517    // Parts(String file)
1518    Parts(String file, String host) {
1519        int ind = file.indexOf('#');
1520        ref = ind < 0 ? null: file.substring(ind + 1);
1521        file = ind < 0 ? file: file.substring(0, ind);
1522        int q = file.lastIndexOf('?');
1523        if (q != -1) {
1524            query = file.substring(q+1);
1525            path = file.substring(0, q);
1526        } else {
1527            path = file;
1528        }
1529        // BEGIN Android-changed: App compat. Prepend '/' if host is null / empty.
1530        if (path != null && path.length() > 0 && path.charAt(0) != '/' &&
1531            host != null && !host.isEmpty()) {
1532            path = '/' + path;
1533        }
1534        // END Android-changed: App compat. Prepend '/' if host is null / empty.
1535    }
1536
1537    String getPath() {
1538        return path;
1539    }
1540
1541    String getQuery() {
1542        return query;
1543    }
1544
1545    String getRef() {
1546        return ref;
1547    }
1548}
1549
1550final class UrlDeserializedState {
1551    private final String protocol;
1552    private final String host;
1553    private final int port;
1554    private final String authority;
1555    private final String file;
1556    private final String ref;
1557    private final int hashCode;
1558
1559    public UrlDeserializedState(String protocol,
1560                                String host, int port,
1561                                String authority, String file,
1562                                String ref, int hashCode) {
1563        this.protocol = protocol;
1564        this.host = host;
1565        this.port = port;
1566        this.authority = authority;
1567        this.file = file;
1568        this.ref = ref;
1569        this.hashCode = hashCode;
1570    }
1571
1572    String getProtocol() {
1573        return protocol;
1574    }
1575
1576    String getHost() {
1577        return host;
1578    }
1579
1580    String getAuthority () {
1581        return authority;
1582    }
1583
1584    int getPort() {
1585        return port;
1586    }
1587
1588    String getFile () {
1589        return file;
1590    }
1591
1592    String getRef () {
1593        return ref;
1594    }
1595
1596    int getHashCode () {
1597        return hashCode;
1598    }
1599
1600    String reconstituteUrlString() {
1601
1602        // pre-compute length of StringBuilder
1603        int len = protocol.length() + 1;
1604        if (authority != null && authority.length() > 0)
1605            len += 2 + authority.length();
1606        if (file != null) {
1607            len += file.length();
1608        }
1609        if (ref != null)
1610            len += 1 + ref.length();
1611        StringBuilder result = new StringBuilder(len);
1612        result.append(protocol);
1613        result.append(":");
1614        if (authority != null && authority.length() > 0) {
1615            result.append("//");
1616            result.append(authority);
1617        }
1618        if (file != null) {
1619            result.append(file);
1620        }
1621        if (ref != null) {
1622            result.append("#");
1623            result.append(ref);
1624        }
1625        return result.toString();
1626    }
1627}
1628