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