1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.os.Environment;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.StrictMode;
23import android.util.Log;
24
25import java.io.File;
26import java.io.IOException;
27import java.io.UnsupportedEncodingException;
28import java.net.URLEncoder;
29import java.nio.charset.StandardCharsets;
30import java.util.AbstractList;
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.LinkedHashSet;
34import java.util.List;
35import java.util.Locale;
36import java.util.Objects;
37import java.util.RandomAccess;
38import java.util.Set;
39
40import libcore.net.UriCodec;
41
42/**
43 * Immutable URI reference. A URI reference includes a URI and a fragment, the
44 * component of the URI following a '#'. Builds and parses URI references
45 * which conform to
46 * <a href="http://www.faqs.org/rfcs/rfc2396.html">RFC 2396</a>.
47 *
48 * <p>In the interest of performance, this class performs little to no
49 * validation. Behavior is undefined for invalid input. This class is very
50 * forgiving--in the face of invalid input, it will return garbage
51 * rather than throw an exception unless otherwise specified.
52 */
53public abstract class Uri implements Parcelable, Comparable<Uri> {
54
55    /*
56
57    This class aims to do as little up front work as possible. To accomplish
58    that, we vary the implementation depending on what the user passes in.
59    For example, we have one implementation if the user passes in a
60    URI string (StringUri) and another if the user passes in the
61    individual components (OpaqueUri).
62
63    *Concurrency notes*: Like any truly immutable object, this class is safe
64    for concurrent use. This class uses a caching pattern in some places where
65    it doesn't use volatile or synchronized. This is safe to do with ints
66    because getting or setting an int is atomic. It's safe to do with a String
67    because the internal fields are final and the memory model guarantees other
68    threads won't see a partially initialized instance. We are not guaranteed
69    that some threads will immediately see changes from other threads on
70    certain platforms, but we don't mind if those threads reconstruct the
71    cached result. As a result, we get thread safe caching with no concurrency
72    overhead, which means the most common case, access from a single thread,
73    is as fast as possible.
74
75    From the Java Language spec.:
76
77    "17.5 Final Field Semantics
78
79    ... when the object is seen by another thread, that thread will always
80    see the correctly constructed version of that object's final fields.
81    It will also see versions of any object or array referenced by
82    those final fields that are at least as up-to-date as the final fields
83    are."
84
85    In that same vein, all non-transient fields within Uri
86    implementations should be final and immutable so as to ensure true
87    immutability for clients even when they don't use proper concurrency
88    control.
89
90    For reference, from RFC 2396:
91
92    "4.3. Parsing a URI Reference
93
94       A URI reference is typically parsed according to the four main
95       components and fragment identifier in order to determine what
96       components are present and whether the reference is relative or
97       absolute.  The individual components are then parsed for their
98       subparts and, if not opaque, to verify their validity.
99
100       Although the BNF defines what is allowed in each component, it is
101       ambiguous in terms of differentiating between an authority component
102       and a path component that begins with two slash characters.  The
103       greedy algorithm is used for disambiguation: the left-most matching
104       rule soaks up as much of the URI reference string as it is capable of
105       matching.  In other words, the authority component wins."
106
107    The "four main components" of a hierarchical URI consist of
108    <scheme>://<authority><path>?<query>
109
110    */
111
112    /** Log tag. */
113    private static final String LOG = Uri.class.getSimpleName();
114
115    /**
116     * NOTE: EMPTY accesses this field during its own initialization, so this
117     * field *must* be initialized first, or else EMPTY will see a null value!
118     *
119     * Placeholder for strings which haven't been cached. This enables us
120     * to cache null. We intentionally create a new String instance so we can
121     * compare its identity and there is no chance we will confuse it with
122     * user data.
123     */
124    @SuppressWarnings("RedundantStringConstructorCall")
125    private static final String NOT_CACHED = new String("NOT CACHED");
126
127    /**
128     * The empty URI, equivalent to "".
129     */
130    public static final Uri EMPTY = new HierarchicalUri(null, Part.NULL,
131            PathPart.EMPTY, Part.NULL, Part.NULL);
132
133    /**
134     * Prevents external subclassing.
135     */
136    private Uri() {}
137
138    /**
139     * Returns true if this URI is hierarchical like "http://google.com".
140     * Absolute URIs are hierarchical if the scheme-specific part starts with
141     * a '/'. Relative URIs are always hierarchical.
142     */
143    public abstract boolean isHierarchical();
144
145    /**
146     * Returns true if this URI is opaque like "mailto:nobody@google.com". The
147     * scheme-specific part of an opaque URI cannot start with a '/'.
148     */
149    public boolean isOpaque() {
150        return !isHierarchical();
151    }
152
153    /**
154     * Returns true if this URI is relative, i.e.&nbsp;if it doesn't contain an
155     * explicit scheme.
156     *
157     * @return true if this URI is relative, false if it's absolute
158     */
159    public abstract boolean isRelative();
160
161    /**
162     * Returns true if this URI is absolute, i.e.&nbsp;if it contains an
163     * explicit scheme.
164     *
165     * @return true if this URI is absolute, false if it's relative
166     */
167    public boolean isAbsolute() {
168        return !isRelative();
169    }
170
171    /**
172     * Gets the scheme of this URI. Example: "http"
173     *
174     * @return the scheme or null if this is a relative URI
175     */
176    public abstract String getScheme();
177
178    /**
179     * Gets the scheme-specific part of this URI, i.e.&nbsp;everything between
180     * the scheme separator ':' and the fragment separator '#'. If this is a
181     * relative URI, this method returns the entire URI. Decodes escaped octets.
182     *
183     * <p>Example: "//www.google.com/search?q=android"
184     *
185     * @return the decoded scheme-specific-part
186     */
187    public abstract String getSchemeSpecificPart();
188
189    /**
190     * Gets the scheme-specific part of this URI, i.e.&nbsp;everything between
191     * the scheme separator ':' and the fragment separator '#'. If this is a
192     * relative URI, this method returns the entire URI. Leaves escaped octets
193     * intact.
194     *
195     * <p>Example: "//www.google.com/search?q=android"
196     *
197     * @return the decoded scheme-specific-part
198     */
199    public abstract String getEncodedSchemeSpecificPart();
200
201    /**
202     * Gets the decoded authority part of this URI. For
203     * server addresses, the authority is structured as follows:
204     * {@code [ userinfo '@' ] host [ ':' port ]}
205     *
206     * <p>Examples: "google.com", "bob@google.com:80"
207     *
208     * @return the authority for this URI or null if not present
209     */
210    public abstract String getAuthority();
211
212    /**
213     * Gets the encoded authority part of this URI. For
214     * server addresses, the authority is structured as follows:
215     * {@code [ userinfo '@' ] host [ ':' port ]}
216     *
217     * <p>Examples: "google.com", "bob@google.com:80"
218     *
219     * @return the authority for this URI or null if not present
220     */
221    public abstract String getEncodedAuthority();
222
223    /**
224     * Gets the decoded user information from the authority.
225     * For example, if the authority is "nobody@google.com", this method will
226     * return "nobody".
227     *
228     * @return the user info for this URI or null if not present
229     */
230    public abstract String getUserInfo();
231
232    /**
233     * Gets the encoded user information from the authority.
234     * For example, if the authority is "nobody@google.com", this method will
235     * return "nobody".
236     *
237     * @return the user info for this URI or null if not present
238     */
239    public abstract String getEncodedUserInfo();
240
241    /**
242     * Gets the encoded host from the authority for this URI. For example,
243     * if the authority is "bob@google.com", this method will return
244     * "google.com".
245     *
246     * @return the host for this URI or null if not present
247     */
248    public abstract String getHost();
249
250    /**
251     * Gets the port from the authority for this URI. For example,
252     * if the authority is "google.com:80", this method will return 80.
253     *
254     * @return the port for this URI or -1 if invalid or not present
255     */
256    public abstract int getPort();
257
258    /**
259     * Gets the decoded path.
260     *
261     * @return the decoded path, or null if this is not a hierarchical URI
262     * (like "mailto:nobody@google.com") or the URI is invalid
263     */
264    public abstract String getPath();
265
266    /**
267     * Gets the encoded path.
268     *
269     * @return the encoded path, or null if this is not a hierarchical URI
270     * (like "mailto:nobody@google.com") or the URI is invalid
271     */
272    public abstract String getEncodedPath();
273
274    /**
275     * Gets the decoded query component from this URI. The query comes after
276     * the query separator ('?') and before the fragment separator ('#'). This
277     * method would return "q=android" for
278     * "http://www.google.com/search?q=android".
279     *
280     * @return the decoded query or null if there isn't one
281     */
282    public abstract String getQuery();
283
284    /**
285     * Gets the encoded query component from this URI. The query comes after
286     * the query separator ('?') and before the fragment separator ('#'). This
287     * method would return "q=android" for
288     * "http://www.google.com/search?q=android".
289     *
290     * @return the encoded query or null if there isn't one
291     */
292    public abstract String getEncodedQuery();
293
294    /**
295     * Gets the decoded fragment part of this URI, everything after the '#'.
296     *
297     * @return the decoded fragment or null if there isn't one
298     */
299    public abstract String getFragment();
300
301    /**
302     * Gets the encoded fragment part of this URI, everything after the '#'.
303     *
304     * @return the encoded fragment or null if there isn't one
305     */
306    public abstract String getEncodedFragment();
307
308    /**
309     * Gets the decoded path segments.
310     *
311     * @return decoded path segments, each without a leading or trailing '/'
312     */
313    public abstract List<String> getPathSegments();
314
315    /**
316     * Gets the decoded last segment in the path.
317     *
318     * @return the decoded last segment or null if the path is empty
319     */
320    public abstract String getLastPathSegment();
321
322    /**
323     * Compares this Uri to another object for equality. Returns true if the
324     * encoded string representations of this Uri and the given Uri are
325     * equal. Case counts. Paths are not normalized. If one Uri specifies a
326     * default port explicitly and the other leaves it implicit, they will not
327     * be considered equal.
328     */
329    public boolean equals(Object o) {
330        if (!(o instanceof Uri)) {
331            return false;
332        }
333
334        Uri other = (Uri) o;
335
336        return toString().equals(other.toString());
337    }
338
339    /**
340     * Hashes the encoded string represention of this Uri consistently with
341     * {@link #equals(Object)}.
342     */
343    public int hashCode() {
344        return toString().hashCode();
345    }
346
347    /**
348     * Compares the string representation of this Uri with that of
349     * another.
350     */
351    public int compareTo(Uri other) {
352        return toString().compareTo(other.toString());
353    }
354
355    /**
356     * Returns the encoded string representation of this URI.
357     * Example: "http://google.com/"
358     */
359    public abstract String toString();
360
361    /**
362     * Return a string representation of the URI that is safe to print
363     * to logs and other places where PII should be avoided.
364     * @hide
365     */
366    public String toSafeString() {
367        String scheme = getScheme();
368        String ssp = getSchemeSpecificPart();
369        if (scheme != null) {
370            if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
371                    || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
372                    || scheme.equalsIgnoreCase("mailto")) {
373                StringBuilder builder = new StringBuilder(64);
374                builder.append(scheme);
375                builder.append(':');
376                if (ssp != null) {
377                    for (int i=0; i<ssp.length(); i++) {
378                        char c = ssp.charAt(i);
379                        if (c == '-' || c == '@' || c == '.') {
380                            builder.append(c);
381                        } else {
382                            builder.append('x');
383                        }
384                    }
385                }
386                return builder.toString();
387            }
388        }
389        // Not a sensitive scheme, but let's still be conservative about
390        // the data we include -- only the ssp, not the query params or
391        // fragment, because those can often have sensitive info.
392        StringBuilder builder = new StringBuilder(64);
393        if (scheme != null) {
394            builder.append(scheme);
395            builder.append(':');
396        }
397        if (ssp != null) {
398            builder.append(ssp);
399        }
400        return builder.toString();
401    }
402
403    /**
404     * Constructs a new builder, copying the attributes from this Uri.
405     */
406    public abstract Builder buildUpon();
407
408    /** Index of a component which was not found. */
409    private final static int NOT_FOUND = -1;
410
411    /** Placeholder value for an index which hasn't been calculated yet. */
412    private final static int NOT_CALCULATED = -2;
413
414    /**
415     * Error message presented when a user tries to treat an opaque URI as
416     * hierarchical.
417     */
418    private static final String NOT_HIERARCHICAL
419            = "This isn't a hierarchical URI.";
420
421    /** Default encoding. */
422    private static final String DEFAULT_ENCODING = "UTF-8";
423
424    /**
425     * Creates a Uri which parses the given encoded URI string.
426     *
427     * @param uriString an RFC 2396-compliant, encoded URI
428     * @throws NullPointerException if uriString is null
429     * @return Uri for this given uri string
430     */
431    public static Uri parse(String uriString) {
432        return new StringUri(uriString);
433    }
434
435    /**
436     * Creates a Uri from a file. The URI has the form
437     * "file://<absolute path>". Encodes path characters with the exception of
438     * '/'.
439     *
440     * <p>Example: "file:///tmp/android.txt"
441     *
442     * @throws NullPointerException if file is null
443     * @return a Uri for the given file
444     */
445    public static Uri fromFile(File file) {
446        if (file == null) {
447            throw new NullPointerException("file");
448        }
449
450        PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
451        return new HierarchicalUri(
452                "file", Part.EMPTY, path, Part.NULL, Part.NULL);
453    }
454
455    /**
456     * An implementation which wraps a String URI. This URI can be opaque or
457     * hierarchical, but we extend AbstractHierarchicalUri in case we need
458     * the hierarchical functionality.
459     */
460    private static class StringUri extends AbstractHierarchicalUri {
461
462        /** Used in parcelling. */
463        static final int TYPE_ID = 1;
464
465        /** URI string representation. */
466        private final String uriString;
467
468        private StringUri(String uriString) {
469            if (uriString == null) {
470                throw new NullPointerException("uriString");
471            }
472
473            this.uriString = uriString;
474        }
475
476        static Uri readFrom(Parcel parcel) {
477            return new StringUri(parcel.readString());
478        }
479
480        public int describeContents() {
481            return 0;
482        }
483
484        public void writeToParcel(Parcel parcel, int flags) {
485            parcel.writeInt(TYPE_ID);
486            parcel.writeString(uriString);
487        }
488
489        /** Cached scheme separator index. */
490        private volatile int cachedSsi = NOT_CALCULATED;
491
492        /** Finds the first ':'. Returns -1 if none found. */
493        private int findSchemeSeparator() {
494            return cachedSsi == NOT_CALCULATED
495                    ? cachedSsi = uriString.indexOf(':')
496                    : cachedSsi;
497        }
498
499        /** Cached fragment separator index. */
500        private volatile int cachedFsi = NOT_CALCULATED;
501
502        /** Finds the first '#'. Returns -1 if none found. */
503        private int findFragmentSeparator() {
504            return cachedFsi == NOT_CALCULATED
505                    ? cachedFsi = uriString.indexOf('#', findSchemeSeparator())
506                    : cachedFsi;
507        }
508
509        public boolean isHierarchical() {
510            int ssi = findSchemeSeparator();
511
512            if (ssi == NOT_FOUND) {
513                // All relative URIs are hierarchical.
514                return true;
515            }
516
517            if (uriString.length() == ssi + 1) {
518                // No ssp.
519                return false;
520            }
521
522            // If the ssp starts with a '/', this is hierarchical.
523            return uriString.charAt(ssi + 1) == '/';
524        }
525
526        public boolean isRelative() {
527            // Note: We return true if the index is 0
528            return findSchemeSeparator() == NOT_FOUND;
529        }
530
531        private volatile String scheme = NOT_CACHED;
532
533        public String getScheme() {
534            @SuppressWarnings("StringEquality")
535            boolean cached = (scheme != NOT_CACHED);
536            return cached ? scheme : (scheme = parseScheme());
537        }
538
539        private String parseScheme() {
540            int ssi = findSchemeSeparator();
541            return ssi == NOT_FOUND ? null : uriString.substring(0, ssi);
542        }
543
544        private Part ssp;
545
546        private Part getSsp() {
547            return ssp == null ? ssp = Part.fromEncoded(parseSsp()) : ssp;
548        }
549
550        public String getEncodedSchemeSpecificPart() {
551            return getSsp().getEncoded();
552        }
553
554        public String getSchemeSpecificPart() {
555            return getSsp().getDecoded();
556        }
557
558        private String parseSsp() {
559            int ssi = findSchemeSeparator();
560            int fsi = findFragmentSeparator();
561
562            // Return everything between ssi and fsi.
563            return fsi == NOT_FOUND
564                    ? uriString.substring(ssi + 1)
565                    : uriString.substring(ssi + 1, fsi);
566        }
567
568        private Part authority;
569
570        private Part getAuthorityPart() {
571            if (authority == null) {
572                String encodedAuthority
573                        = parseAuthority(this.uriString, findSchemeSeparator());
574                return authority = Part.fromEncoded(encodedAuthority);
575            }
576
577            return authority;
578        }
579
580        public String getEncodedAuthority() {
581            return getAuthorityPart().getEncoded();
582        }
583
584        public String getAuthority() {
585            return getAuthorityPart().getDecoded();
586        }
587
588        private PathPart path;
589
590        private PathPart getPathPart() {
591            return path == null
592                    ? path = PathPart.fromEncoded(parsePath())
593                    : path;
594        }
595
596        public String getPath() {
597            return getPathPart().getDecoded();
598        }
599
600        public String getEncodedPath() {
601            return getPathPart().getEncoded();
602        }
603
604        public List<String> getPathSegments() {
605            return getPathPart().getPathSegments();
606        }
607
608        private String parsePath() {
609            String uriString = this.uriString;
610            int ssi = findSchemeSeparator();
611
612            // If the URI is absolute.
613            if (ssi > -1) {
614                // Is there anything after the ':'?
615                boolean schemeOnly = ssi + 1 == uriString.length();
616                if (schemeOnly) {
617                    // Opaque URI.
618                    return null;
619                }
620
621                // A '/' after the ':' means this is hierarchical.
622                if (uriString.charAt(ssi + 1) != '/') {
623                    // Opaque URI.
624                    return null;
625                }
626            } else {
627                // All relative URIs are hierarchical.
628            }
629
630            return parsePath(uriString, ssi);
631        }
632
633        private Part query;
634
635        private Part getQueryPart() {
636            return query == null
637                    ? query = Part.fromEncoded(parseQuery()) : query;
638        }
639
640        public String getEncodedQuery() {
641            return getQueryPart().getEncoded();
642        }
643
644        private String parseQuery() {
645            // It doesn't make sense to cache this index. We only ever
646            // calculate it once.
647            int qsi = uriString.indexOf('?', findSchemeSeparator());
648            if (qsi == NOT_FOUND) {
649                return null;
650            }
651
652            int fsi = findFragmentSeparator();
653
654            if (fsi == NOT_FOUND) {
655                return uriString.substring(qsi + 1);
656            }
657
658            if (fsi < qsi) {
659                // Invalid.
660                return null;
661            }
662
663            return uriString.substring(qsi + 1, fsi);
664        }
665
666        public String getQuery() {
667            return getQueryPart().getDecoded();
668        }
669
670        private Part fragment;
671
672        private Part getFragmentPart() {
673            return fragment == null
674                    ? fragment = Part.fromEncoded(parseFragment()) : fragment;
675        }
676
677        public String getEncodedFragment() {
678            return getFragmentPart().getEncoded();
679        }
680
681        private String parseFragment() {
682            int fsi = findFragmentSeparator();
683            return fsi == NOT_FOUND ? null : uriString.substring(fsi + 1);
684        }
685
686        public String getFragment() {
687            return getFragmentPart().getDecoded();
688        }
689
690        public String toString() {
691            return uriString;
692        }
693
694        /**
695         * Parses an authority out of the given URI string.
696         *
697         * @param uriString URI string
698         * @param ssi scheme separator index, -1 for a relative URI
699         *
700         * @return the authority or null if none is found
701         */
702        static String parseAuthority(String uriString, int ssi) {
703            int length = uriString.length();
704
705            // If "//" follows the scheme separator, we have an authority.
706            if (length > ssi + 2
707                    && uriString.charAt(ssi + 1) == '/'
708                    && uriString.charAt(ssi + 2) == '/') {
709                // We have an authority.
710
711                // Look for the start of the path, query, or fragment, or the
712                // end of the string.
713                int end = ssi + 3;
714                LOOP: while (end < length) {
715                    switch (uriString.charAt(end)) {
716                        case '/': // Start of path
717                        case '?': // Start of query
718                        case '#': // Start of fragment
719                            break LOOP;
720                    }
721                    end++;
722                }
723
724                return uriString.substring(ssi + 3, end);
725            } else {
726                return null;
727            }
728
729        }
730
731        /**
732         * Parses a path out of this given URI string.
733         *
734         * @param uriString URI string
735         * @param ssi scheme separator index, -1 for a relative URI
736         *
737         * @return the path
738         */
739        static String parsePath(String uriString, int ssi) {
740            int length = uriString.length();
741
742            // Find start of path.
743            int pathStart;
744            if (length > ssi + 2
745                    && uriString.charAt(ssi + 1) == '/'
746                    && uriString.charAt(ssi + 2) == '/') {
747                // Skip over authority to path.
748                pathStart = ssi + 3;
749                LOOP: while (pathStart < length) {
750                    switch (uriString.charAt(pathStart)) {
751                        case '?': // Start of query
752                        case '#': // Start of fragment
753                            return ""; // Empty path.
754                        case '/': // Start of path!
755                            break LOOP;
756                    }
757                    pathStart++;
758                }
759            } else {
760                // Path starts immediately after scheme separator.
761                pathStart = ssi + 1;
762            }
763
764            // Find end of path.
765            int pathEnd = pathStart;
766            LOOP: while (pathEnd < length) {
767                switch (uriString.charAt(pathEnd)) {
768                    case '?': // Start of query
769                    case '#': // Start of fragment
770                        break LOOP;
771                }
772                pathEnd++;
773            }
774
775            return uriString.substring(pathStart, pathEnd);
776        }
777
778        public Builder buildUpon() {
779            if (isHierarchical()) {
780                return new Builder()
781                        .scheme(getScheme())
782                        .authority(getAuthorityPart())
783                        .path(getPathPart())
784                        .query(getQueryPart())
785                        .fragment(getFragmentPart());
786            } else {
787                return new Builder()
788                        .scheme(getScheme())
789                        .opaquePart(getSsp())
790                        .fragment(getFragmentPart());
791            }
792        }
793    }
794
795    /**
796     * Creates an opaque Uri from the given components. Encodes the ssp
797     * which means this method cannot be used to create hierarchical URIs.
798     *
799     * @param scheme of the URI
800     * @param ssp scheme-specific-part, everything between the
801     *  scheme separator (':') and the fragment separator ('#'), which will
802     *  get encoded
803     * @param fragment fragment, everything after the '#', null if undefined,
804     *  will get encoded
805     *
806     * @throws NullPointerException if scheme or ssp is null
807     * @return Uri composed of the given scheme, ssp, and fragment
808     *
809     * @see Builder if you don't want the ssp and fragment to be encoded
810     */
811    public static Uri fromParts(String scheme, String ssp,
812            String fragment) {
813        if (scheme == null) {
814            throw new NullPointerException("scheme");
815        }
816        if (ssp == null) {
817            throw new NullPointerException("ssp");
818        }
819
820        return new OpaqueUri(scheme, Part.fromDecoded(ssp),
821                Part.fromDecoded(fragment));
822    }
823
824    /**
825     * Opaque URI.
826     */
827    private static class OpaqueUri extends Uri {
828
829        /** Used in parcelling. */
830        static final int TYPE_ID = 2;
831
832        private final String scheme;
833        private final Part ssp;
834        private final Part fragment;
835
836        private OpaqueUri(String scheme, Part ssp, Part fragment) {
837            this.scheme = scheme;
838            this.ssp = ssp;
839            this.fragment = fragment == null ? Part.NULL : fragment;
840        }
841
842        static Uri readFrom(Parcel parcel) {
843            return new OpaqueUri(
844                parcel.readString(),
845                Part.readFrom(parcel),
846                Part.readFrom(parcel)
847            );
848        }
849
850        public int describeContents() {
851            return 0;
852        }
853
854        public void writeToParcel(Parcel parcel, int flags) {
855            parcel.writeInt(TYPE_ID);
856            parcel.writeString(scheme);
857            ssp.writeTo(parcel);
858            fragment.writeTo(parcel);
859        }
860
861        public boolean isHierarchical() {
862            return false;
863        }
864
865        public boolean isRelative() {
866            return scheme == null;
867        }
868
869        public String getScheme() {
870            return this.scheme;
871        }
872
873        public String getEncodedSchemeSpecificPart() {
874            return ssp.getEncoded();
875        }
876
877        public String getSchemeSpecificPart() {
878            return ssp.getDecoded();
879        }
880
881        public String getAuthority() {
882            return null;
883        }
884
885        public String getEncodedAuthority() {
886            return null;
887        }
888
889        public String getPath() {
890            return null;
891        }
892
893        public String getEncodedPath() {
894            return null;
895        }
896
897        public String getQuery() {
898            return null;
899        }
900
901        public String getEncodedQuery() {
902            return null;
903        }
904
905        public String getFragment() {
906            return fragment.getDecoded();
907        }
908
909        public String getEncodedFragment() {
910            return fragment.getEncoded();
911        }
912
913        public List<String> getPathSegments() {
914            return Collections.emptyList();
915        }
916
917        public String getLastPathSegment() {
918            return null;
919        }
920
921        public String getUserInfo() {
922            return null;
923        }
924
925        public String getEncodedUserInfo() {
926            return null;
927        }
928
929        public String getHost() {
930            return null;
931        }
932
933        public int getPort() {
934            return -1;
935        }
936
937        private volatile String cachedString = NOT_CACHED;
938
939        public String toString() {
940            @SuppressWarnings("StringEquality")
941            boolean cached = cachedString != NOT_CACHED;
942            if (cached) {
943                return cachedString;
944            }
945
946            StringBuilder sb = new StringBuilder();
947
948            sb.append(scheme).append(':');
949            sb.append(getEncodedSchemeSpecificPart());
950
951            if (!fragment.isEmpty()) {
952                sb.append('#').append(fragment.getEncoded());
953            }
954
955            return cachedString = sb.toString();
956        }
957
958        public Builder buildUpon() {
959            return new Builder()
960                    .scheme(this.scheme)
961                    .opaquePart(this.ssp)
962                    .fragment(this.fragment);
963        }
964    }
965
966    /**
967     * Wrapper for path segment array.
968     */
969    static class PathSegments extends AbstractList<String>
970            implements RandomAccess {
971
972        static final PathSegments EMPTY = new PathSegments(null, 0);
973
974        final String[] segments;
975        final int size;
976
977        PathSegments(String[] segments, int size) {
978            this.segments = segments;
979            this.size = size;
980        }
981
982        public String get(int index) {
983            if (index >= size) {
984                throw new IndexOutOfBoundsException();
985            }
986
987            return segments[index];
988        }
989
990        public int size() {
991            return this.size;
992        }
993    }
994
995    /**
996     * Builds PathSegments.
997     */
998    static class PathSegmentsBuilder {
999
1000        String[] segments;
1001        int size = 0;
1002
1003        void add(String segment) {
1004            if (segments == null) {
1005                segments = new String[4];
1006            } else if (size + 1 == segments.length) {
1007                String[] expanded = new String[segments.length * 2];
1008                System.arraycopy(segments, 0, expanded, 0, segments.length);
1009                segments = expanded;
1010            }
1011
1012            segments[size++] = segment;
1013        }
1014
1015        PathSegments build() {
1016            if (segments == null) {
1017                return PathSegments.EMPTY;
1018            }
1019
1020            try {
1021                return new PathSegments(segments, size);
1022            } finally {
1023                // Makes sure this doesn't get reused.
1024                segments = null;
1025            }
1026        }
1027    }
1028
1029    /**
1030     * Support for hierarchical URIs.
1031     */
1032    private abstract static class AbstractHierarchicalUri extends Uri {
1033
1034        public String getLastPathSegment() {
1035            // TODO: If we haven't parsed all of the segments already, just
1036            // grab the last one directly so we only allocate one string.
1037
1038            List<String> segments = getPathSegments();
1039            int size = segments.size();
1040            if (size == 0) {
1041                return null;
1042            }
1043            return segments.get(size - 1);
1044        }
1045
1046        private Part userInfo;
1047
1048        private Part getUserInfoPart() {
1049            return userInfo == null
1050                    ? userInfo = Part.fromEncoded(parseUserInfo()) : userInfo;
1051        }
1052
1053        public final String getEncodedUserInfo() {
1054            return getUserInfoPart().getEncoded();
1055        }
1056
1057        private String parseUserInfo() {
1058            String authority = getEncodedAuthority();
1059            if (authority == null) {
1060                return null;
1061            }
1062
1063            int end = authority.indexOf('@');
1064            return end == NOT_FOUND ? null : authority.substring(0, end);
1065        }
1066
1067        public String getUserInfo() {
1068            return getUserInfoPart().getDecoded();
1069        }
1070
1071        private volatile String host = NOT_CACHED;
1072
1073        public String getHost() {
1074            @SuppressWarnings("StringEquality")
1075            boolean cached = (host != NOT_CACHED);
1076            return cached ? host
1077                    : (host = parseHost());
1078        }
1079
1080        private String parseHost() {
1081            String authority = getEncodedAuthority();
1082            if (authority == null) {
1083                return null;
1084            }
1085
1086            // Parse out user info and then port.
1087            int userInfoSeparator = authority.indexOf('@');
1088            int portSeparator = authority.indexOf(':', userInfoSeparator);
1089
1090            String encodedHost = portSeparator == NOT_FOUND
1091                    ? authority.substring(userInfoSeparator + 1)
1092                    : authority.substring(userInfoSeparator + 1, portSeparator);
1093
1094            return decode(encodedHost);
1095        }
1096
1097        private volatile int port = NOT_CALCULATED;
1098
1099        public int getPort() {
1100            return port == NOT_CALCULATED
1101                    ? port = parsePort()
1102                    : port;
1103        }
1104
1105        private int parsePort() {
1106            String authority = getEncodedAuthority();
1107            if (authority == null) {
1108                return -1;
1109            }
1110
1111            // Make sure we look for the port separtor *after* the user info
1112            // separator. We have URLs with a ':' in the user info.
1113            int userInfoSeparator = authority.indexOf('@');
1114            int portSeparator = authority.indexOf(':', userInfoSeparator);
1115
1116            if (portSeparator == NOT_FOUND) {
1117                return -1;
1118            }
1119
1120            String portString = decode(authority.substring(portSeparator + 1));
1121            try {
1122                return Integer.parseInt(portString);
1123            } catch (NumberFormatException e) {
1124                Log.w(LOG, "Error parsing port string.", e);
1125                return -1;
1126            }
1127        }
1128    }
1129
1130    /**
1131     * Hierarchical Uri.
1132     */
1133    private static class HierarchicalUri extends AbstractHierarchicalUri {
1134
1135        /** Used in parcelling. */
1136        static final int TYPE_ID = 3;
1137
1138        private final String scheme; // can be null
1139        private final Part authority;
1140        private final PathPart path;
1141        private final Part query;
1142        private final Part fragment;
1143
1144        private HierarchicalUri(String scheme, Part authority, PathPart path,
1145                Part query, Part fragment) {
1146            this.scheme = scheme;
1147            this.authority = Part.nonNull(authority);
1148            this.path = path == null ? PathPart.NULL : path;
1149            this.query = Part.nonNull(query);
1150            this.fragment = Part.nonNull(fragment);
1151        }
1152
1153        static Uri readFrom(Parcel parcel) {
1154            return new HierarchicalUri(
1155                parcel.readString(),
1156                Part.readFrom(parcel),
1157                PathPart.readFrom(parcel),
1158                Part.readFrom(parcel),
1159                Part.readFrom(parcel)
1160            );
1161        }
1162
1163        public int describeContents() {
1164            return 0;
1165        }
1166
1167        public void writeToParcel(Parcel parcel, int flags) {
1168            parcel.writeInt(TYPE_ID);
1169            parcel.writeString(scheme);
1170            authority.writeTo(parcel);
1171            path.writeTo(parcel);
1172            query.writeTo(parcel);
1173            fragment.writeTo(parcel);
1174        }
1175
1176        public boolean isHierarchical() {
1177            return true;
1178        }
1179
1180        public boolean isRelative() {
1181            return scheme == null;
1182        }
1183
1184        public String getScheme() {
1185            return scheme;
1186        }
1187
1188        private Part ssp;
1189
1190        private Part getSsp() {
1191            return ssp == null
1192                    ? ssp = Part.fromEncoded(makeSchemeSpecificPart()) : ssp;
1193        }
1194
1195        public String getEncodedSchemeSpecificPart() {
1196            return getSsp().getEncoded();
1197        }
1198
1199        public String getSchemeSpecificPart() {
1200            return getSsp().getDecoded();
1201        }
1202
1203        /**
1204         * Creates the encoded scheme-specific part from its sub parts.
1205         */
1206        private String makeSchemeSpecificPart() {
1207            StringBuilder builder = new StringBuilder();
1208            appendSspTo(builder);
1209            return builder.toString();
1210        }
1211
1212        private void appendSspTo(StringBuilder builder) {
1213            String encodedAuthority = authority.getEncoded();
1214            if (encodedAuthority != null) {
1215                // Even if the authority is "", we still want to append "//".
1216                builder.append("//").append(encodedAuthority);
1217            }
1218
1219            String encodedPath = path.getEncoded();
1220            if (encodedPath != null) {
1221                builder.append(encodedPath);
1222            }
1223
1224            if (!query.isEmpty()) {
1225                builder.append('?').append(query.getEncoded());
1226            }
1227        }
1228
1229        public String getAuthority() {
1230            return this.authority.getDecoded();
1231        }
1232
1233        public String getEncodedAuthority() {
1234            return this.authority.getEncoded();
1235        }
1236
1237        public String getEncodedPath() {
1238            return this.path.getEncoded();
1239        }
1240
1241        public String getPath() {
1242            return this.path.getDecoded();
1243        }
1244
1245        public String getQuery() {
1246            return this.query.getDecoded();
1247        }
1248
1249        public String getEncodedQuery() {
1250            return this.query.getEncoded();
1251        }
1252
1253        public String getFragment() {
1254            return this.fragment.getDecoded();
1255        }
1256
1257        public String getEncodedFragment() {
1258            return this.fragment.getEncoded();
1259        }
1260
1261        public List<String> getPathSegments() {
1262            return this.path.getPathSegments();
1263        }
1264
1265        private volatile String uriString = NOT_CACHED;
1266
1267        @Override
1268        public String toString() {
1269            @SuppressWarnings("StringEquality")
1270            boolean cached = (uriString != NOT_CACHED);
1271            return cached ? uriString
1272                    : (uriString = makeUriString());
1273        }
1274
1275        private String makeUriString() {
1276            StringBuilder builder = new StringBuilder();
1277
1278            if (scheme != null) {
1279                builder.append(scheme).append(':');
1280            }
1281
1282            appendSspTo(builder);
1283
1284            if (!fragment.isEmpty()) {
1285                builder.append('#').append(fragment.getEncoded());
1286            }
1287
1288            return builder.toString();
1289        }
1290
1291        public Builder buildUpon() {
1292            return new Builder()
1293                    .scheme(scheme)
1294                    .authority(authority)
1295                    .path(path)
1296                    .query(query)
1297                    .fragment(fragment);
1298        }
1299    }
1300
1301    /**
1302     * Helper class for building or manipulating URI references. Not safe for
1303     * concurrent use.
1304     *
1305     * <p>An absolute hierarchical URI reference follows the pattern:
1306     * {@code <scheme>://<authority><absolute path>?<query>#<fragment>}
1307     *
1308     * <p>Relative URI references (which are always hierarchical) follow one
1309     * of two patterns: {@code <relative or absolute path>?<query>#<fragment>}
1310     * or {@code //<authority><absolute path>?<query>#<fragment>}
1311     *
1312     * <p>An opaque URI follows this pattern:
1313     * {@code <scheme>:<opaque part>#<fragment>}
1314     *
1315     * <p>Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI.
1316     */
1317    public static final class Builder {
1318
1319        private String scheme;
1320        private Part opaquePart;
1321        private Part authority;
1322        private PathPart path;
1323        private Part query;
1324        private Part fragment;
1325
1326        /**
1327         * Constructs a new Builder.
1328         */
1329        public Builder() {}
1330
1331        /**
1332         * Sets the scheme.
1333         *
1334         * @param scheme name or {@code null} if this is a relative Uri
1335         */
1336        public Builder scheme(String scheme) {
1337            this.scheme = scheme;
1338            return this;
1339        }
1340
1341        Builder opaquePart(Part opaquePart) {
1342            this.opaquePart = opaquePart;
1343            return this;
1344        }
1345
1346        /**
1347         * Encodes and sets the given opaque scheme-specific-part.
1348         *
1349         * @param opaquePart decoded opaque part
1350         */
1351        public Builder opaquePart(String opaquePart) {
1352            return opaquePart(Part.fromDecoded(opaquePart));
1353        }
1354
1355        /**
1356         * Sets the previously encoded opaque scheme-specific-part.
1357         *
1358         * @param opaquePart encoded opaque part
1359         */
1360        public Builder encodedOpaquePart(String opaquePart) {
1361            return opaquePart(Part.fromEncoded(opaquePart));
1362        }
1363
1364        Builder authority(Part authority) {
1365            // This URI will be hierarchical.
1366            this.opaquePart = null;
1367
1368            this.authority = authority;
1369            return this;
1370        }
1371
1372        /**
1373         * Encodes and sets the authority.
1374         */
1375        public Builder authority(String authority) {
1376            return authority(Part.fromDecoded(authority));
1377        }
1378
1379        /**
1380         * Sets the previously encoded authority.
1381         */
1382        public Builder encodedAuthority(String authority) {
1383            return authority(Part.fromEncoded(authority));
1384        }
1385
1386        Builder path(PathPart path) {
1387            // This URI will be hierarchical.
1388            this.opaquePart = null;
1389
1390            this.path = path;
1391            return this;
1392        }
1393
1394        /**
1395         * Sets the path. Leaves '/' characters intact but encodes others as
1396         * necessary.
1397         *
1398         * <p>If the path is not null and doesn't start with a '/', and if
1399         * you specify a scheme and/or authority, the builder will prepend the
1400         * given path with a '/'.
1401         */
1402        public Builder path(String path) {
1403            return path(PathPart.fromDecoded(path));
1404        }
1405
1406        /**
1407         * Sets the previously encoded path.
1408         *
1409         * <p>If the path is not null and doesn't start with a '/', and if
1410         * you specify a scheme and/or authority, the builder will prepend the
1411         * given path with a '/'.
1412         */
1413        public Builder encodedPath(String path) {
1414            return path(PathPart.fromEncoded(path));
1415        }
1416
1417        /**
1418         * Encodes the given segment and appends it to the path.
1419         */
1420        public Builder appendPath(String newSegment) {
1421            return path(PathPart.appendDecodedSegment(path, newSegment));
1422        }
1423
1424        /**
1425         * Appends the given segment to the path.
1426         */
1427        public Builder appendEncodedPath(String newSegment) {
1428            return path(PathPart.appendEncodedSegment(path, newSegment));
1429        }
1430
1431        Builder query(Part query) {
1432            // This URI will be hierarchical.
1433            this.opaquePart = null;
1434
1435            this.query = query;
1436            return this;
1437        }
1438
1439        /**
1440         * Encodes and sets the query.
1441         */
1442        public Builder query(String query) {
1443            return query(Part.fromDecoded(query));
1444        }
1445
1446        /**
1447         * Sets the previously encoded query.
1448         */
1449        public Builder encodedQuery(String query) {
1450            return query(Part.fromEncoded(query));
1451        }
1452
1453        Builder fragment(Part fragment) {
1454            this.fragment = fragment;
1455            return this;
1456        }
1457
1458        /**
1459         * Encodes and sets the fragment.
1460         */
1461        public Builder fragment(String fragment) {
1462            return fragment(Part.fromDecoded(fragment));
1463        }
1464
1465        /**
1466         * Sets the previously encoded fragment.
1467         */
1468        public Builder encodedFragment(String fragment) {
1469            return fragment(Part.fromEncoded(fragment));
1470        }
1471
1472        /**
1473         * Encodes the key and value and then appends the parameter to the
1474         * query string.
1475         *
1476         * @param key which will be encoded
1477         * @param value which will be encoded
1478         */
1479        public Builder appendQueryParameter(String key, String value) {
1480            // This URI will be hierarchical.
1481            this.opaquePart = null;
1482
1483            String encodedParameter = encode(key, null) + "="
1484                    + encode(value, null);
1485
1486            if (query == null) {
1487                query = Part.fromEncoded(encodedParameter);
1488                return this;
1489            }
1490
1491            String oldQuery = query.getEncoded();
1492            if (oldQuery == null || oldQuery.length() == 0) {
1493                query = Part.fromEncoded(encodedParameter);
1494            } else {
1495                query = Part.fromEncoded(oldQuery + "&" + encodedParameter);
1496            }
1497
1498            return this;
1499        }
1500
1501        /**
1502         * Clears the the previously set query.
1503         */
1504        public Builder clearQuery() {
1505          return query((Part) null);
1506        }
1507
1508        /**
1509         * Constructs a Uri with the current attributes.
1510         *
1511         * @throws UnsupportedOperationException if the URI is opaque and the
1512         *  scheme is null
1513         */
1514        public Uri build() {
1515            if (opaquePart != null) {
1516                if (this.scheme == null) {
1517                    throw new UnsupportedOperationException(
1518                            "An opaque URI must have a scheme.");
1519                }
1520
1521                return new OpaqueUri(scheme, opaquePart, fragment);
1522            } else {
1523                // Hierarchical URIs should not return null for getPath().
1524                PathPart path = this.path;
1525                if (path == null || path == PathPart.NULL) {
1526                    path = PathPart.EMPTY;
1527                } else {
1528                    // If we have a scheme and/or authority, the path must
1529                    // be absolute. Prepend it with a '/' if necessary.
1530                    if (hasSchemeOrAuthority()) {
1531                        path = PathPart.makeAbsolute(path);
1532                    }
1533                }
1534
1535                return new HierarchicalUri(
1536                        scheme, authority, path, query, fragment);
1537            }
1538        }
1539
1540        private boolean hasSchemeOrAuthority() {
1541            return scheme != null
1542                    || (authority != null && authority != Part.NULL);
1543
1544        }
1545
1546        @Override
1547        public String toString() {
1548            return build().toString();
1549        }
1550    }
1551
1552    /**
1553     * Returns a set of the unique names of all query parameters. Iterating
1554     * over the set will return the names in order of their first occurrence.
1555     *
1556     * @throws UnsupportedOperationException if this isn't a hierarchical URI
1557     *
1558     * @return a set of decoded names
1559     */
1560    public Set<String> getQueryParameterNames() {
1561        if (isOpaque()) {
1562            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
1563        }
1564
1565        String query = getEncodedQuery();
1566        if (query == null) {
1567            return Collections.emptySet();
1568        }
1569
1570        Set<String> names = new LinkedHashSet<String>();
1571        int start = 0;
1572        do {
1573            int next = query.indexOf('&', start);
1574            int end = (next == -1) ? query.length() : next;
1575
1576            int separator = query.indexOf('=', start);
1577            if (separator > end || separator == -1) {
1578                separator = end;
1579            }
1580
1581            String name = query.substring(start, separator);
1582            names.add(decode(name));
1583
1584            // Move start to end of name.
1585            start = end + 1;
1586        } while (start < query.length());
1587
1588        return Collections.unmodifiableSet(names);
1589    }
1590
1591    /**
1592     * Searches the query string for parameter values with the given key.
1593     *
1594     * @param key which will be encoded
1595     *
1596     * @throws UnsupportedOperationException if this isn't a hierarchical URI
1597     * @throws NullPointerException if key is null
1598     * @return a list of decoded values
1599     */
1600    public List<String> getQueryParameters(String key) {
1601        if (isOpaque()) {
1602            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
1603        }
1604        if (key == null) {
1605          throw new NullPointerException("key");
1606        }
1607
1608        String query = getEncodedQuery();
1609        if (query == null) {
1610            return Collections.emptyList();
1611        }
1612
1613        String encodedKey;
1614        try {
1615            encodedKey = URLEncoder.encode(key, DEFAULT_ENCODING);
1616        } catch (UnsupportedEncodingException e) {
1617            throw new AssertionError(e);
1618        }
1619
1620        ArrayList<String> values = new ArrayList<String>();
1621
1622        int start = 0;
1623        do {
1624            int nextAmpersand = query.indexOf('&', start);
1625            int end = nextAmpersand != -1 ? nextAmpersand : query.length();
1626
1627            int separator = query.indexOf('=', start);
1628            if (separator > end || separator == -1) {
1629                separator = end;
1630            }
1631
1632            if (separator - start == encodedKey.length()
1633                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
1634                if (separator == end) {
1635                  values.add("");
1636                } else {
1637                  values.add(decode(query.substring(separator + 1, end)));
1638                }
1639            }
1640
1641            // Move start to end of name.
1642            if (nextAmpersand != -1) {
1643                start = nextAmpersand + 1;
1644            } else {
1645                break;
1646            }
1647        } while (true);
1648
1649        return Collections.unmodifiableList(values);
1650    }
1651
1652    /**
1653     * Searches the query string for the first value with the given key.
1654     *
1655     * <p><strong>Warning:</strong> Prior to Ice Cream Sandwich, this decoded
1656     * the '+' character as '+' rather than ' '.
1657     *
1658     * @param key which will be encoded
1659     * @throws UnsupportedOperationException if this isn't a hierarchical URI
1660     * @throws NullPointerException if key is null
1661     * @return the decoded value or null if no parameter is found
1662     */
1663    public String getQueryParameter(String key) {
1664        if (isOpaque()) {
1665            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
1666        }
1667        if (key == null) {
1668            throw new NullPointerException("key");
1669        }
1670
1671        final String query = getEncodedQuery();
1672        if (query == null) {
1673            return null;
1674        }
1675
1676        final String encodedKey = encode(key, null);
1677        final int length = query.length();
1678        int start = 0;
1679        do {
1680            int nextAmpersand = query.indexOf('&', start);
1681            int end = nextAmpersand != -1 ? nextAmpersand : length;
1682
1683            int separator = query.indexOf('=', start);
1684            if (separator > end || separator == -1) {
1685                separator = end;
1686            }
1687
1688            if (separator - start == encodedKey.length()
1689                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
1690                if (separator == end) {
1691                    return "";
1692                } else {
1693                    String encodedValue = query.substring(separator + 1, end);
1694                    return UriCodec.decode(encodedValue, true, StandardCharsets.UTF_8, false);
1695                }
1696            }
1697
1698            // Move start to end of name.
1699            if (nextAmpersand != -1) {
1700                start = nextAmpersand + 1;
1701            } else {
1702                break;
1703            }
1704        } while (true);
1705        return null;
1706    }
1707
1708    /**
1709     * Searches the query string for the first value with the given key and interprets it
1710     * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
1711     * else is interpreted as <code>true</code>.
1712     *
1713     * @param key which will be decoded
1714     * @param defaultValue the default value to return if there is no query parameter for key
1715     * @return the boolean interpretation of the query parameter key
1716     */
1717    public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
1718        String flag = getQueryParameter(key);
1719        if (flag == null) {
1720            return defaultValue;
1721        }
1722        flag = flag.toLowerCase(Locale.ROOT);
1723        return (!"false".equals(flag) && !"0".equals(flag));
1724    }
1725
1726    /**
1727     * Return an equivalent URI with a lowercase scheme component.
1728     * This aligns the Uri with Android best practices for
1729     * intent filtering.
1730     *
1731     * <p>For example, "HTTP://www.android.com" becomes
1732     * "http://www.android.com"
1733     *
1734     * <p>All URIs received from outside Android (such as user input,
1735     * or external sources like Bluetooth, NFC, or the Internet) should
1736     * be normalized before they are used to create an Intent.
1737     *
1738     * <p class="note">This method does <em>not</em> validate bad URI's,
1739     * or 'fix' poorly formatted URI's - so do not use it for input validation.
1740     * A Uri will always be returned, even if the Uri is badly formatted to
1741     * begin with and a scheme component cannot be found.
1742     *
1743     * @return normalized Uri (never null)
1744     * @see {@link android.content.Intent#setData}
1745     * @see {@link #setNormalizedData}
1746     */
1747    public Uri normalizeScheme() {
1748        String scheme = getScheme();
1749        if (scheme == null) return this;  // give up
1750        String lowerScheme = scheme.toLowerCase(Locale.ROOT);
1751        if (scheme.equals(lowerScheme)) return this;  // no change
1752
1753        return buildUpon().scheme(lowerScheme).build();
1754    }
1755
1756    /** Identifies a null parcelled Uri. */
1757    private static final int NULL_TYPE_ID = 0;
1758
1759    /**
1760     * Reads Uris from Parcels.
1761     */
1762    public static final Parcelable.Creator<Uri> CREATOR
1763            = new Parcelable.Creator<Uri>() {
1764        public Uri createFromParcel(Parcel in) {
1765            int type = in.readInt();
1766            switch (type) {
1767                case NULL_TYPE_ID: return null;
1768                case StringUri.TYPE_ID: return StringUri.readFrom(in);
1769                case OpaqueUri.TYPE_ID: return OpaqueUri.readFrom(in);
1770                case HierarchicalUri.TYPE_ID:
1771                    return HierarchicalUri.readFrom(in);
1772            }
1773
1774            throw new IllegalArgumentException("Unknown URI type: " + type);
1775        }
1776
1777        public Uri[] newArray(int size) {
1778            return new Uri[size];
1779        }
1780    };
1781
1782    /**
1783     * Writes a Uri to a Parcel.
1784     *
1785     * @param out parcel to write to
1786     * @param uri to write, can be null
1787     */
1788    public static void writeToParcel(Parcel out, Uri uri) {
1789        if (uri == null) {
1790            out.writeInt(NULL_TYPE_ID);
1791        } else {
1792            uri.writeToParcel(out, 0);
1793        }
1794    }
1795
1796    private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
1797
1798    /**
1799     * Encodes characters in the given string as '%'-escaped octets
1800     * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
1801     * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
1802     * all other characters.
1803     *
1804     * @param s string to encode
1805     * @return an encoded version of s suitable for use as a URI component,
1806     *  or null if s is null
1807     */
1808    public static String encode(String s) {
1809        return encode(s, null);
1810    }
1811
1812    /**
1813     * Encodes characters in the given string as '%'-escaped octets
1814     * using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
1815     * ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
1816     * all other characters with the exception of those specified in the
1817     * allow argument.
1818     *
1819     * @param s string to encode
1820     * @param allow set of additional characters to allow in the encoded form,
1821     *  null if no characters should be skipped
1822     * @return an encoded version of s suitable for use as a URI component,
1823     *  or null if s is null
1824     */
1825    public static String encode(String s, String allow) {
1826        if (s == null) {
1827            return null;
1828        }
1829
1830        // Lazily-initialized buffers.
1831        StringBuilder encoded = null;
1832
1833        int oldLength = s.length();
1834
1835        // This loop alternates between copying over allowed characters and
1836        // encoding in chunks. This results in fewer method calls and
1837        // allocations than encoding one character at a time.
1838        int current = 0;
1839        while (current < oldLength) {
1840            // Start in "copying" mode where we copy over allowed chars.
1841
1842            // Find the next character which needs to be encoded.
1843            int nextToEncode = current;
1844            while (nextToEncode < oldLength
1845                    && isAllowed(s.charAt(nextToEncode), allow)) {
1846                nextToEncode++;
1847            }
1848
1849            // If there's nothing more to encode...
1850            if (nextToEncode == oldLength) {
1851                if (current == 0) {
1852                    // We didn't need to encode anything!
1853                    return s;
1854                } else {
1855                    // Presumably, we've already done some encoding.
1856                    encoded.append(s, current, oldLength);
1857                    return encoded.toString();
1858                }
1859            }
1860
1861            if (encoded == null) {
1862                encoded = new StringBuilder();
1863            }
1864
1865            if (nextToEncode > current) {
1866                // Append allowed characters leading up to this point.
1867                encoded.append(s, current, nextToEncode);
1868            } else {
1869                // assert nextToEncode == current
1870            }
1871
1872            // Switch to "encoding" mode.
1873
1874            // Find the next allowed character.
1875            current = nextToEncode;
1876            int nextAllowed = current + 1;
1877            while (nextAllowed < oldLength
1878                    && !isAllowed(s.charAt(nextAllowed), allow)) {
1879                nextAllowed++;
1880            }
1881
1882            // Convert the substring to bytes and encode the bytes as
1883            // '%'-escaped octets.
1884            String toEncode = s.substring(current, nextAllowed);
1885            try {
1886                byte[] bytes = toEncode.getBytes(DEFAULT_ENCODING);
1887                int bytesLength = bytes.length;
1888                for (int i = 0; i < bytesLength; i++) {
1889                    encoded.append('%');
1890                    encoded.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4]);
1891                    encoded.append(HEX_DIGITS[bytes[i] & 0xf]);
1892                }
1893            } catch (UnsupportedEncodingException e) {
1894                throw new AssertionError(e);
1895            }
1896
1897            current = nextAllowed;
1898        }
1899
1900        // Encoded could still be null at this point if s is empty.
1901        return encoded == null ? s : encoded.toString();
1902    }
1903
1904    /**
1905     * Returns true if the given character is allowed.
1906     *
1907     * @param c character to check
1908     * @param allow characters to allow
1909     * @return true if the character is allowed or false if it should be
1910     *  encoded
1911     */
1912    private static boolean isAllowed(char c, String allow) {
1913        return (c >= 'A' && c <= 'Z')
1914                || (c >= 'a' && c <= 'z')
1915                || (c >= '0' && c <= '9')
1916                || "_-!.~'()*".indexOf(c) != NOT_FOUND
1917                || (allow != null && allow.indexOf(c) != NOT_FOUND);
1918    }
1919
1920    /**
1921     * Decodes '%'-escaped octets in the given string using the UTF-8 scheme.
1922     * Replaces invalid octets with the unicode replacement character
1923     * ("\\uFFFD").
1924     *
1925     * @param s encoded string to decode
1926     * @return the given string with escaped octets decoded, or null if
1927     *  s is null
1928     */
1929    public static String decode(String s) {
1930        if (s == null) {
1931            return null;
1932        }
1933        return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
1934    }
1935
1936    /**
1937     * Support for part implementations.
1938     */
1939    static abstract class AbstractPart {
1940
1941        /**
1942         * Enum which indicates which representation of a given part we have.
1943         */
1944        static class Representation {
1945            static final int BOTH = 0;
1946            static final int ENCODED = 1;
1947            static final int DECODED = 2;
1948        }
1949
1950        volatile String encoded;
1951        volatile String decoded;
1952
1953        AbstractPart(String encoded, String decoded) {
1954            this.encoded = encoded;
1955            this.decoded = decoded;
1956        }
1957
1958        abstract String getEncoded();
1959
1960        final String getDecoded() {
1961            @SuppressWarnings("StringEquality")
1962            boolean hasDecoded = decoded != NOT_CACHED;
1963            return hasDecoded ? decoded : (decoded = decode(encoded));
1964        }
1965
1966        final void writeTo(Parcel parcel) {
1967            @SuppressWarnings("StringEquality")
1968            boolean hasEncoded = encoded != NOT_CACHED;
1969
1970            @SuppressWarnings("StringEquality")
1971            boolean hasDecoded = decoded != NOT_CACHED;
1972
1973            if (hasEncoded && hasDecoded) {
1974                parcel.writeInt(Representation.BOTH);
1975                parcel.writeString(encoded);
1976                parcel.writeString(decoded);
1977            } else if (hasEncoded) {
1978                parcel.writeInt(Representation.ENCODED);
1979                parcel.writeString(encoded);
1980            } else if (hasDecoded) {
1981                parcel.writeInt(Representation.DECODED);
1982                parcel.writeString(decoded);
1983            } else {
1984                throw new IllegalArgumentException("Neither encoded nor decoded");
1985            }
1986        }
1987    }
1988
1989    /**
1990     * Immutable wrapper of encoded and decoded versions of a URI part. Lazily
1991     * creates the encoded or decoded version from the other.
1992     */
1993    static class Part extends AbstractPart {
1994
1995        /** A part with null values. */
1996        static final Part NULL = new EmptyPart(null);
1997
1998        /** A part with empty strings for values. */
1999        static final Part EMPTY = new EmptyPart("");
2000
2001        private Part(String encoded, String decoded) {
2002            super(encoded, decoded);
2003        }
2004
2005        boolean isEmpty() {
2006            return false;
2007        }
2008
2009        String getEncoded() {
2010            @SuppressWarnings("StringEquality")
2011            boolean hasEncoded = encoded != NOT_CACHED;
2012            return hasEncoded ? encoded : (encoded = encode(decoded));
2013        }
2014
2015        static Part readFrom(Parcel parcel) {
2016            int representation = parcel.readInt();
2017            switch (representation) {
2018                case Representation.BOTH:
2019                    return from(parcel.readString(), parcel.readString());
2020                case Representation.ENCODED:
2021                    return fromEncoded(parcel.readString());
2022                case Representation.DECODED:
2023                    return fromDecoded(parcel.readString());
2024                default:
2025                    throw new IllegalArgumentException("Unknown representation: "
2026                            + representation);
2027            }
2028        }
2029
2030        /**
2031         * Returns given part or {@link #NULL} if the given part is null.
2032         */
2033        static Part nonNull(Part part) {
2034            return part == null ? NULL : part;
2035        }
2036
2037        /**
2038         * Creates a part from the encoded string.
2039         *
2040         * @param encoded part string
2041         */
2042        static Part fromEncoded(String encoded) {
2043            return from(encoded, NOT_CACHED);
2044        }
2045
2046        /**
2047         * Creates a part from the decoded string.
2048         *
2049         * @param decoded part string
2050         */
2051        static Part fromDecoded(String decoded) {
2052            return from(NOT_CACHED, decoded);
2053        }
2054
2055        /**
2056         * Creates a part from the encoded and decoded strings.
2057         *
2058         * @param encoded part string
2059         * @param decoded part string
2060         */
2061        static Part from(String encoded, String decoded) {
2062            // We have to check both encoded and decoded in case one is
2063            // NOT_CACHED.
2064
2065            if (encoded == null) {
2066                return NULL;
2067            }
2068            if (encoded.length() == 0) {
2069                return EMPTY;
2070            }
2071
2072            if (decoded == null) {
2073                return NULL;
2074            }
2075            if (decoded .length() == 0) {
2076                return EMPTY;
2077            }
2078
2079            return new Part(encoded, decoded);
2080        }
2081
2082        private static class EmptyPart extends Part {
2083            public EmptyPart(String value) {
2084                super(value, value);
2085            }
2086
2087            @Override
2088            boolean isEmpty() {
2089                return true;
2090            }
2091        }
2092    }
2093
2094    /**
2095     * Immutable wrapper of encoded and decoded versions of a path part. Lazily
2096     * creates the encoded or decoded version from the other.
2097     */
2098    static class PathPart extends AbstractPart {
2099
2100        /** A part with null values. */
2101        static final PathPart NULL = new PathPart(null, null);
2102
2103        /** A part with empty strings for values. */
2104        static final PathPart EMPTY = new PathPart("", "");
2105
2106        private PathPart(String encoded, String decoded) {
2107            super(encoded, decoded);
2108        }
2109
2110        String getEncoded() {
2111            @SuppressWarnings("StringEquality")
2112            boolean hasEncoded = encoded != NOT_CACHED;
2113
2114            // Don't encode '/'.
2115            return hasEncoded ? encoded : (encoded = encode(decoded, "/"));
2116        }
2117
2118        /**
2119         * Cached path segments. This doesn't need to be volatile--we don't
2120         * care if other threads see the result.
2121         */
2122        private PathSegments pathSegments;
2123
2124        /**
2125         * Gets the individual path segments. Parses them if necessary.
2126         *
2127         * @return parsed path segments or null if this isn't a hierarchical
2128         *  URI
2129         */
2130        PathSegments getPathSegments() {
2131            if (pathSegments != null) {
2132                return pathSegments;
2133            }
2134
2135            String path = getEncoded();
2136            if (path == null) {
2137                return pathSegments = PathSegments.EMPTY;
2138            }
2139
2140            PathSegmentsBuilder segmentBuilder = new PathSegmentsBuilder();
2141
2142            int previous = 0;
2143            int current;
2144            while ((current = path.indexOf('/', previous)) > -1) {
2145                // This check keeps us from adding a segment if the path starts
2146                // '/' and an empty segment for "//".
2147                if (previous < current) {
2148                    String decodedSegment
2149                            = decode(path.substring(previous, current));
2150                    segmentBuilder.add(decodedSegment);
2151                }
2152                previous = current + 1;
2153            }
2154
2155            // Add in the final path segment.
2156            if (previous < path.length()) {
2157                segmentBuilder.add(decode(path.substring(previous)));
2158            }
2159
2160            return pathSegments = segmentBuilder.build();
2161        }
2162
2163        static PathPart appendEncodedSegment(PathPart oldPart,
2164                String newSegment) {
2165            // If there is no old path, should we make the new path relative
2166            // or absolute? I pick absolute.
2167
2168            if (oldPart == null) {
2169                // No old path.
2170                return fromEncoded("/" + newSegment);
2171            }
2172
2173            String oldPath = oldPart.getEncoded();
2174
2175            if (oldPath == null) {
2176                oldPath = "";
2177            }
2178
2179            int oldPathLength = oldPath.length();
2180            String newPath;
2181            if (oldPathLength == 0) {
2182                // No old path.
2183                newPath = "/" + newSegment;
2184            } else if (oldPath.charAt(oldPathLength - 1) == '/') {
2185                newPath = oldPath + newSegment;
2186            } else {
2187                newPath = oldPath + "/" + newSegment;
2188            }
2189
2190            return fromEncoded(newPath);
2191        }
2192
2193        static PathPart appendDecodedSegment(PathPart oldPart, String decoded) {
2194            String encoded = encode(decoded);
2195
2196            // TODO: Should we reuse old PathSegments? Probably not.
2197            return appendEncodedSegment(oldPart, encoded);
2198        }
2199
2200        static PathPart readFrom(Parcel parcel) {
2201            int representation = parcel.readInt();
2202            switch (representation) {
2203                case Representation.BOTH:
2204                    return from(parcel.readString(), parcel.readString());
2205                case Representation.ENCODED:
2206                    return fromEncoded(parcel.readString());
2207                case Representation.DECODED:
2208                    return fromDecoded(parcel.readString());
2209                default:
2210                    throw new IllegalArgumentException("Bad representation: " + representation);
2211            }
2212        }
2213
2214        /**
2215         * Creates a path from the encoded string.
2216         *
2217         * @param encoded part string
2218         */
2219        static PathPart fromEncoded(String encoded) {
2220            return from(encoded, NOT_CACHED);
2221        }
2222
2223        /**
2224         * Creates a path from the decoded string.
2225         *
2226         * @param decoded part string
2227         */
2228        static PathPart fromDecoded(String decoded) {
2229            return from(NOT_CACHED, decoded);
2230        }
2231
2232        /**
2233         * Creates a path from the encoded and decoded strings.
2234         *
2235         * @param encoded part string
2236         * @param decoded part string
2237         */
2238        static PathPart from(String encoded, String decoded) {
2239            if (encoded == null) {
2240                return NULL;
2241            }
2242
2243            if (encoded.length() == 0) {
2244                return EMPTY;
2245            }
2246
2247            return new PathPart(encoded, decoded);
2248        }
2249
2250        /**
2251         * Prepends path values with "/" if they're present, not empty, and
2252         * they don't already start with "/".
2253         */
2254        static PathPart makeAbsolute(PathPart oldPart) {
2255            @SuppressWarnings("StringEquality")
2256            boolean encodedCached = oldPart.encoded != NOT_CACHED;
2257
2258            // We don't care which version we use, and we don't want to force
2259            // unneccessary encoding/decoding.
2260            String oldPath = encodedCached ? oldPart.encoded : oldPart.decoded;
2261
2262            if (oldPath == null || oldPath.length() == 0
2263                    || oldPath.startsWith("/")) {
2264                return oldPart;
2265            }
2266
2267            // Prepend encoded string if present.
2268            String newEncoded = encodedCached
2269                    ? "/" + oldPart.encoded : NOT_CACHED;
2270
2271            // Prepend decoded string if present.
2272            @SuppressWarnings("StringEquality")
2273            boolean decodedCached = oldPart.decoded != NOT_CACHED;
2274            String newDecoded = decodedCached
2275                    ? "/" + oldPart.decoded
2276                    : NOT_CACHED;
2277
2278            return new PathPart(newEncoded, newDecoded);
2279        }
2280    }
2281
2282    /**
2283     * Creates a new Uri by appending an already-encoded path segment to a
2284     * base Uri.
2285     *
2286     * @param baseUri Uri to append path segment to
2287     * @param pathSegment encoded path segment to append
2288     * @return a new Uri based on baseUri with the given segment appended to
2289     *  the path
2290     * @throws NullPointerException if baseUri is null
2291     */
2292    public static Uri withAppendedPath(Uri baseUri, String pathSegment) {
2293        Builder builder = baseUri.buildUpon();
2294        builder = builder.appendEncodedPath(pathSegment);
2295        return builder.build();
2296    }
2297
2298    /**
2299     * If this {@link Uri} is {@code file://}, then resolve and return its
2300     * canonical path. Also fixes legacy emulated storage paths so they are
2301     * usable across user boundaries. Should always be called from the app
2302     * process before sending elsewhere.
2303     *
2304     * @hide
2305     */
2306    public Uri getCanonicalUri() {
2307        if ("file".equals(getScheme())) {
2308            final String canonicalPath;
2309            try {
2310                canonicalPath = new File(getPath()).getCanonicalPath();
2311            } catch (IOException e) {
2312                return this;
2313            }
2314
2315            if (Environment.isExternalStorageEmulated()) {
2316                final String legacyPath = Environment.getLegacyExternalStorageDirectory()
2317                        .toString();
2318
2319                // Splice in user-specific path when legacy path is found
2320                if (canonicalPath.startsWith(legacyPath)) {
2321                    return Uri.fromFile(new File(
2322                            Environment.getExternalStorageDirectory().toString(),
2323                            canonicalPath.substring(legacyPath.length() + 1)));
2324                }
2325            }
2326
2327            return Uri.fromFile(new File(canonicalPath));
2328        } else {
2329            return this;
2330        }
2331    }
2332
2333    /**
2334     * If this is a {@code file://} Uri, it will be reported to
2335     * {@link StrictMode}.
2336     *
2337     * @hide
2338     */
2339    public void checkFileUriExposed(String location) {
2340        if ("file".equals(getScheme())) {
2341            StrictMode.onFileUriExposed(location);
2342        }
2343    }
2344
2345    /**
2346     * Test if this is a path prefix match against the given Uri. Verifies that
2347     * scheme, authority, and atomic path segments match.
2348     *
2349     * @hide
2350     */
2351    public boolean isPathPrefixMatch(Uri prefix) {
2352        if (!Objects.equals(getScheme(), prefix.getScheme())) return false;
2353        if (!Objects.equals(getAuthority(), prefix.getAuthority())) return false;
2354
2355        List<String> seg = getPathSegments();
2356        List<String> prefixSeg = prefix.getPathSegments();
2357
2358        final int prefixSize = prefixSeg.size();
2359        if (seg.size() < prefixSize) return false;
2360
2361        for (int i = 0; i < prefixSize; i++) {
2362            if (!Objects.equals(seg.get(i), prefixSeg.get(i))) {
2363                return false;
2364            }
2365        }
2366
2367        return true;
2368    }
2369}
2370