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