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