Home | History | Annotate | Download | only in net
      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 
     17 package android.net;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.content.ContentResolver;
     22 import android.content.Context;
     23 import android.database.ContentObserver;
     24 import android.net.ProxyProperties;
     25 import android.os.Handler;
     26 import android.os.SystemProperties;
     27 import android.text.TextUtils;
     28 import android.provider.Settings;
     29 import android.util.Log;
     30 
     31 import java.net.InetAddress;
     32 import java.net.InetSocketAddress;
     33 import java.net.ProxySelector;
     34 import java.net.SocketAddress;
     35 import java.net.URI;
     36 import java.net.UnknownHostException;
     37 import java.util.concurrent.locks.ReadWriteLock;
     38 import java.util.concurrent.locks.ReentrantReadWriteLock;
     39 import java.util.List;
     40 import java.util.regex.Matcher;
     41 import java.util.regex.Pattern;
     42 
     43 import junit.framework.Assert;
     44 
     45 import org.apache.http.conn.routing.HttpRoute;
     46 import org.apache.http.conn.routing.HttpRoutePlanner;
     47 import org.apache.http.conn.scheme.SchemeRegistry;
     48 import org.apache.http.HttpHost;
     49 import org.apache.http.HttpRequest;
     50 import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
     51 import org.apache.http.protocol.HttpContext;
     52 
     53 /**
     54  * A convenience class for accessing the user and default proxy
     55  * settings.
     56  */
     57 public final class Proxy {
     58 
     59     // Set to true to enable extra debugging.
     60     private static final boolean DEBUG = false;
     61     private static final String TAG = "Proxy";
     62 
     63     /**
     64      * Used to notify an app that's caching the default connection proxy
     65      * that either the default connection or its proxy has changed.
     66      * The intent will have the following extra value:</p>
     67      * <ul>
     68      *   <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy.  Non-null,
     69      *                                   though if the proxy is undefined the host string
     70      *                                   will be empty.
     71      * </ul>
     72      *
     73      * <p class="note">This is a protected intent that can only be sent by the system
     74      */
     75     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     76     public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
     77     /** {@hide} **/
     78     public static final String EXTRA_PROXY_INFO = "proxy";
     79 
     80     private static ConnectivityManager sConnectivityManager = null;
     81 
     82     // Hostname / IP REGEX validation
     83     // Matches blank input, ips, and domain names
     84     private static final String NAME_IP_REGEX =
     85         "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
     86 
     87     private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
     88 
     89     private static final Pattern HOSTNAME_PATTERN;
     90 
     91     private static final String EXCLLIST_REGEXP = "$|^(.?" + NAME_IP_REGEX
     92         + ")+(,(.?" + NAME_IP_REGEX + "))*$";
     93 
     94     private static final Pattern EXCLLIST_PATTERN;
     95 
     96     static {
     97         HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
     98         EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
     99     }
    100 
    101     /**
    102      * Return the proxy object to be used for the URL given as parameter.
    103      * @param ctx A Context used to get the settings for the proxy host.
    104      * @param url A URL to be accessed. Used to evaluate exclusion list.
    105      * @return Proxy (java.net) object containing the host name. If the
    106      *         user did not set a hostname it returns the default host.
    107      *         A null value means that no host is to be used.
    108      * {@hide}
    109      */
    110     public static final java.net.Proxy getProxy(Context ctx, String url) {
    111         String host = "";
    112         if (url != null) {
    113             URI uri = URI.create(url);
    114             host = uri.getHost();
    115         }
    116 
    117         if (!isLocalHost(host)) {
    118             if (sConnectivityManager == null) {
    119                 sConnectivityManager = (ConnectivityManager)ctx.getSystemService(
    120                         Context.CONNECTIVITY_SERVICE);
    121             }
    122             if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
    123 
    124             ProxyProperties proxyProperties = sConnectivityManager.getProxy();
    125 
    126             if (proxyProperties != null) {
    127                 if (!proxyProperties.isExcluded(host)) {
    128                     return proxyProperties.makeProxy();
    129                 }
    130             }
    131         }
    132         return java.net.Proxy.NO_PROXY;
    133     }
    134 
    135 
    136     /**
    137      * Return the proxy host set by the user.
    138      * @param ctx A Context used to get the settings for the proxy host.
    139      * @return String containing the host name. If the user did not set a host
    140      *         name it returns the default host. A null value means that no
    141      *         host is to be used.
    142      * @deprecated Use standard java vm proxy values to find the host, port
    143      *         and exclusion list.  This call ignores the exclusion list.
    144      */
    145     public static final String getHost(Context ctx) {
    146         java.net.Proxy proxy = getProxy(ctx, null);
    147         if (proxy == java.net.Proxy.NO_PROXY) return null;
    148         try {
    149             return ((InetSocketAddress)(proxy.address())).getHostName();
    150         } catch (Exception e) {
    151             return null;
    152         }
    153     }
    154 
    155     /**
    156      * Return the proxy port set by the user.
    157      * @param ctx A Context used to get the settings for the proxy port.
    158      * @return The port number to use or -1 if no proxy is to be used.
    159      * @deprecated Use standard java vm proxy values to find the host, port
    160      *         and exclusion list.  This call ignores the exclusion list.
    161      */
    162     public static final int getPort(Context ctx) {
    163         java.net.Proxy proxy = getProxy(ctx, null);
    164         if (proxy == java.net.Proxy.NO_PROXY) return -1;
    165         try {
    166             return ((InetSocketAddress)(proxy.address())).getPort();
    167         } catch (Exception e) {
    168             return -1;
    169         }
    170     }
    171 
    172     /**
    173      * Return the default proxy host specified by the carrier.
    174      * @return String containing the host name or null if there is no proxy for
    175      * this carrier.
    176      * @deprecated Use standard java vm proxy values to find the host, port and
    177      *         exclusion list.  This call ignores the exclusion list and no
    178      *         longer reports only mobile-data apn-based proxy values.
    179      */
    180     public static final String getDefaultHost() {
    181         String host = System.getProperty("http.proxyHost");
    182         if (TextUtils.isEmpty(host)) return null;
    183         return host;
    184     }
    185 
    186     /**
    187      * Return the default proxy port specified by the carrier.
    188      * @return The port number to be used with the proxy host or -1 if there is
    189      * no proxy for this carrier.
    190      * @deprecated Use standard java vm proxy values to find the host, port and
    191      *         exclusion list.  This call ignores the exclusion list and no
    192      *         longer reports only mobile-data apn-based proxy values.
    193      */
    194     public static final int getDefaultPort() {
    195         if (getDefaultHost() == null) return -1;
    196         try {
    197             return Integer.parseInt(System.getProperty("http.proxyPort"));
    198         } catch (NumberFormatException e) {
    199             return -1;
    200         }
    201     }
    202 
    203     /**
    204      * Returns the preferred proxy to be used by clients. This is a wrapper
    205      * around {@link android.net.Proxy#getHost()}.
    206      *
    207      * @param context the context which will be passed to
    208      * {@link android.net.Proxy#getHost()}
    209      * @param url the target URL for the request
    210      * @note Calling this method requires permission
    211      * android.permission.ACCESS_NETWORK_STATE
    212      * @return The preferred proxy to be used by clients, or null if there
    213      * is no proxy.
    214      * {@hide}
    215      */
    216     public static final HttpHost getPreferredHttpHost(Context context,
    217             String url) {
    218         java.net.Proxy prefProxy = getProxy(context, url);
    219         if (prefProxy.equals(java.net.Proxy.NO_PROXY)) {
    220             return null;
    221         } else {
    222             InetSocketAddress sa = (InetSocketAddress)prefProxy.address();
    223             return new HttpHost(sa.getHostName(), sa.getPort(), "http");
    224         }
    225     }
    226 
    227     private static final boolean isLocalHost(String host) {
    228         if (host == null) {
    229             return false;
    230         }
    231         try {
    232             if (host != null) {
    233                 if (host.equalsIgnoreCase("localhost")) {
    234                     return true;
    235                 }
    236                 if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
    237                     return true;
    238                 }
    239             }
    240         } catch (IllegalArgumentException iex) {
    241         }
    242         return false;
    243     }
    244 
    245     /**
    246      * Validate syntax of hostname, port and exclusion list entries
    247      * {@hide}
    248      */
    249     public static void validate(String hostname, String port, String exclList) {
    250         Matcher match = HOSTNAME_PATTERN.matcher(hostname);
    251         Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
    252 
    253         if (!match.matches()) {
    254             throw new IllegalArgumentException();
    255         }
    256 
    257         if (!listMatch.matches()) {
    258             throw new IllegalArgumentException();
    259         }
    260 
    261         if (hostname.length() > 0 && port.length() == 0) {
    262             throw new IllegalArgumentException();
    263         }
    264 
    265         if (port.length() > 0) {
    266             if (hostname.length() == 0) {
    267                 throw new IllegalArgumentException();
    268             }
    269             int portVal = -1;
    270             try {
    271                 portVal = Integer.parseInt(port);
    272             } catch (NumberFormatException ex) {
    273                 throw new IllegalArgumentException();
    274             }
    275             if (portVal <= 0 || portVal > 0xFFFF) {
    276                 throw new IllegalArgumentException();
    277             }
    278         }
    279     }
    280 
    281     static class AndroidProxySelectorRoutePlanner
    282             extends org.apache.http.impl.conn.ProxySelectorRoutePlanner {
    283 
    284         private Context mContext;
    285 
    286         public AndroidProxySelectorRoutePlanner(SchemeRegistry schreg, ProxySelector prosel,
    287                 Context context) {
    288             super(schreg, prosel);
    289             mContext = context;
    290         }
    291 
    292         @Override
    293         protected java.net.Proxy chooseProxy(List<java.net.Proxy> proxies, HttpHost target,
    294                 HttpRequest request, HttpContext context) {
    295             return getProxy(mContext, target.getHostName());
    296         }
    297 
    298         @Override
    299         protected HttpHost determineProxy(HttpHost target, HttpRequest request,
    300                 HttpContext context) {
    301             return getPreferredHttpHost(mContext, target.getHostName());
    302         }
    303 
    304         @Override
    305         public HttpRoute determineRoute(HttpHost target, HttpRequest request,
    306                 HttpContext context) {
    307             HttpHost proxy = getPreferredHttpHost(mContext, target.getHostName());
    308             if (proxy == null) {
    309                 return new HttpRoute(target);
    310             } else {
    311                 return new HttpRoute(target, null, proxy, false);
    312             }
    313         }
    314     }
    315 
    316     /** @hide */
    317     public static final HttpRoutePlanner getAndroidProxySelectorRoutePlanner(Context context) {
    318         AndroidProxySelectorRoutePlanner ret = new AndroidProxySelectorRoutePlanner(
    319                 new SchemeRegistry(), ProxySelector.getDefault(), context);
    320         return ret;
    321     }
    322 
    323     /** @hide */
    324     public static final void setHttpProxySystemProperty(ProxyProperties p) {
    325         String host = null;
    326         String port = null;
    327         String exclList = null;
    328         if (p != null) {
    329             host = p.getHost();
    330             port = Integer.toString(p.getPort());
    331             exclList = p.getExclusionList();
    332         }
    333         setHttpProxySystemProperty(host, port, exclList);
    334     }
    335 
    336     /** @hide */
    337     public static final void setHttpProxySystemProperty(String host, String port, String exclList) {
    338         if (exclList != null) exclList = exclList.replace(",", "|");
    339         if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
    340         if (host != null) {
    341             System.setProperty("http.proxyHost", host);
    342             System.setProperty("https.proxyHost", host);
    343         } else {
    344             System.clearProperty("http.proxyHost");
    345             System.clearProperty("https.proxyHost");
    346         }
    347         if (port != null) {
    348             System.setProperty("http.proxyPort", port);
    349             System.setProperty("https.proxyPort", port);
    350         } else {
    351             System.clearProperty("http.proxyPort");
    352             System.clearProperty("https.proxyPort");
    353         }
    354         if (exclList != null) {
    355             System.setProperty("http.nonProxyHosts", exclList);
    356             System.setProperty("https.nonProxyHosts", exclList);
    357         } else {
    358             System.clearProperty("http.nonProxyHosts");
    359             System.clearProperty("https.nonProxyHosts");
    360         }
    361     }
    362 }
    363