1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* 2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more 3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements. See the NOTICE file distributed with 4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership. 5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0 6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with 7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License. You may obtain a copy of the License at 8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and 15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License. 16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.net; 19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException; 2137b050b2a191930e06cb35667e6fd96af9c78141Neil Fullerimport java.nio.charset.StandardCharsets; 22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.security.cert.Certificate; 23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.Attributes; 24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarEntry; 25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.JarFile; 26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.jar.Manifest; 2737b050b2a191930e06cb35667e6fd96af9c78141Neil Fullerimport libcore.net.UriCodec; 28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/** 30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * This class establishes a connection to a {@code jar:} URL using the {@code 31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JAR} protocol. A {@code JarURLConnection} instance can refer to either a JAR 32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * archive file or to an entry of such a file. {@code jar:} URLs are specified 33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * as follows: <i>jar:{archive-url}!/{entry}</i> where "!/" is called a 34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * separator. This separator is important to determine if an archive or an entry 35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * of an archive is referred. 36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <p> 37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Examples: 38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>Archive: {@code jar:http://www.example.com/applets/archive.jar!/}</li> 39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>File Entry: {@code 40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * jar:http://www.example.com/applets/archive.jar!/test.class}</li> 41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * <li>Directory Entry: {@code 42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * jar:http://www.example.com/applets/archive.jar!/applets/}</li> 43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic abstract class JarURLConnection extends URLConnection { 45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The location part of the represented URL. 48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected URLConnection jarFileURLConnection; 50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 51adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private String entryName; 52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private URL fileURL; 54adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // the file component of the URL 56adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private String file; 57adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Constructs an instance of {@code JarURLConnection} that refers to the 60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * specified URL. 61f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 62adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param url 63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the URL that contains the location to connect to. 64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws MalformedURLException 65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an invalid URL has been entered. 66adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 67adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project protected JarURLConnection(URL url) throws MalformedURLException { 68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project super(url); 6937b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller file = decode(url.getFile()); 7037b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller 71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project int sepIdx; 72f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if ((sepIdx = file.indexOf("!/")) < 0) { 73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project throw new MalformedURLException(); 74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 7537b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller fileURL = new URL(file.substring(0, sepIdx)); 76f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson sepIdx += 2; 77f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson if (file.length() == sepIdx) { 78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return; 79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 80f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson entryName = file.substring(sepIdx, file.length()); 81b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes if (url.getRef() != null) { 82f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes entryName += "#" + url.getRef(); 83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns all attributes of the {@code JarEntry} referenced by this {@code 88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JarURLConnection}. 89f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the attributes of the referenced {@code JarEntry}. 91f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * @throws IOException 92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an I/O exception occurs while retrieving the 93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JAR-entries. 94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public Attributes getAttributes() throws java.io.IOException { 96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project JarEntry jEntry = getJarEntry(); 97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return (jEntry == null) ? null : jEntry.getAttributes(); 98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 101adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns all certificates of the {@code JarEntry} referenced by this 102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code JarURLConnection} instance. This method will return {@code null} 103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * until the {@code InputStream} has been completely verified. 104f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the certificates of the {@code JarEntry} as an array. 106f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * @throws IOException 107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if there is an I/O exception occurs while getting the 108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code JarEntry}. 109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public Certificate[] getCertificates() throws java.io.IOException { 111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project JarEntry jEntry = getJarEntry(); 112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (jEntry == null) { 113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return null; 114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 115adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 116adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return jEntry.getCertificates(); 117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 119adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 120adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the name of the entry referenced by this {@code JarURLConnection}. 121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The return value will be {@code null} if this instance refers to a JAR 122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * file rather than an JAR file entry. 123f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the {@code JarEntry} name this instance refers to. 125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 126adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public String getEntryName() { 127adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return entryName; 128adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 129adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 130adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the {@code JarEntry} object of the entry referenced by this {@code 132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JarURLConnection}. 133f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the referenced {@code JarEntry} object or {@code null} if no 135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * entry name is specified. 136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an error occurs while getting the file or file-entry. 138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public JarEntry getJarEntry() throws IOException { 140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (!connected) { 141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project connect(); 142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (entryName == null) { 144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return null; 145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // The entry must exist since the connect succeeded 147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return getJarFile().getJarEntry(entryName); 148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the manifest file associated with this JAR-URL. 152f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the manifest of the referenced JAR-file. 154adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an error occurs while getting the manifest file. 156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public Manifest getManifest() throws java.io.IOException { 158f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson return (Manifest)getJarFile().getManifest().clone(); 159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the {@code JarFile} object referenced by this {@code 163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JarURLConnection}. 164f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the referenced JarFile object. 166f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * @throws IOException 167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an I/O exception occurs while retrieving the JAR-file. 168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public abstract JarFile getJarFile() throws java.io.IOException; 170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the URL to the JAR-file referenced by this {@code JarURLConnection}. 173f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the URL to the JAR-file or {@code null} if there was an error 175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * retrieving the URL. 176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public URL getJarFileURL() { 178f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson return fileURL; 179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 182adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets all attributes of the manifest file referenced by this {@code 183adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JarURLConnection}. If this instance refers to a JAR-file rather than a 184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JAR-file entry, {@code null} will be returned. 185f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * 186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the attributes of the manifest file or {@code null}. 187f5597e626ecf7949d249dea08c1a2964d890ec11Jesse Wilson * @throws IOException 188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an I/O exception occurs while retrieving the {@code 189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * JarFile}. 190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public Attributes getMainAttributes() throws java.io.IOException { 192adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project Manifest m = getJarFile().getManifest(); 193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return (m == null) ? null : m.getMainAttributes(); 194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 19537b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller 19637b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller private static String decode(String encoded) throws MalformedURLException { 19737b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller try { 19837b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller // "+" means "+" in URLs. i.e. like RFC 3986, not like 19937b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller // MIME application/x-www-form-urlencoded 20037b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller final boolean convertPlus = false; 20137b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller return UriCodec.decode( 20237b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller encoded, convertPlus, StandardCharsets.UTF_8, true /* throwOnFailure */); 20337b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller } catch (IllegalArgumentException e) { 20437b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller throw new MalformedURLException("Unable to decode URL", e); 20537b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller } 20637b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller } 20737b050b2a191930e06cb35667e6fd96af9c78141Neil Fuller 208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project} 209