1 /*
2  * Copyright (C) 2010 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.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.TextUtils;
25 
26 import com.android.net.module.util.ProxyUtils;
27 
28 import java.net.InetSocketAddress;
29 import java.net.URLConnection;
30 import java.util.List;
31 import java.util.Locale;
32 
33 /**
34  * Describes a proxy configuration.
35  *
36  * Proxy configurations are already integrated within the {@code java.net} and
37  * Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use
38  * them automatically.
39  *
40  * Other HTTP stacks will need to obtain the proxy info by watching for the
41  * {@link Proxy#PROXY_CHANGE_ACTION} broadcast and calling methods such as
42  * {@link android.net.ConnectivityManager#getDefaultProxy}.
43  */
44 public class ProxyInfo implements Parcelable {
45 
46     private final String mHost;
47     private final int mPort;
48     private final String mExclusionList;
49     private final String[] mParsedExclusionList;
50     // Uri.EMPTY if none.
51     @NonNull
52     private final Uri mPacFileUrl;
53 
54     /**
55      *@hide
56      */
57     public static final String LOCAL_EXCL_LIST = "";
58     /**
59      *@hide
60      */
61     public static final int LOCAL_PORT = -1;
62     /**
63      *@hide
64      */
65     public static final String LOCAL_HOST = "localhost";
66 
67     /**
68      * Constructs a {@link ProxyInfo} object that points at a Direct proxy
69      * on the specified host and port.
70      */
buildDirectProxy(String host, int port)71     public static ProxyInfo buildDirectProxy(String host, int port) {
72         return new ProxyInfo(host, port, null);
73     }
74 
75     /**
76      * Constructs a {@link ProxyInfo} object that points at a Direct proxy
77      * on the specified host and port.
78      *
79      * The proxy will not be used to access any host in exclusion list, exclList.
80      *
81      * @param exclList Hosts to exclude using the proxy on connections for.  These
82      *                 hosts can use wildcards such as *.example.com.
83      */
buildDirectProxy(String host, int port, List<String> exclList)84     public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) {
85         String[] array = exclList.toArray(new String[exclList.size()]);
86         return new ProxyInfo(host, port, TextUtils.join(",", array), array);
87     }
88 
89     /**
90      * Construct a {@link ProxyInfo} that will download and run the PAC script
91      * at the specified URL.
92      */
buildPacProxy(Uri pacUri)93     public static ProxyInfo buildPacProxy(Uri pacUri) {
94         return new ProxyInfo(pacUri);
95     }
96 
97     /**
98      * Construct a {@link ProxyInfo} object that will download and run the PAC script at the
99      * specified URL and port.
100      */
101     @NonNull
buildPacProxy(@onNull Uri pacUrl, int port)102     public static ProxyInfo buildPacProxy(@NonNull Uri pacUrl, int port) {
103         return new ProxyInfo(pacUrl, port);
104     }
105 
106     /**
107      * Create a ProxyProperties that points at a HTTP Proxy.
108      * @hide
109      */
110     @UnsupportedAppUsage
ProxyInfo(String host, int port, String exclList)111     public ProxyInfo(String host, int port, String exclList) {
112         mHost = host;
113         mPort = port;
114         mExclusionList = exclList;
115         mParsedExclusionList = parseExclusionList(mExclusionList);
116         mPacFileUrl = Uri.EMPTY;
117     }
118 
119     /**
120      * Create a ProxyProperties that points at a PAC URL.
121      * @hide
122      */
ProxyInfo(@onNull Uri pacFileUrl)123     public ProxyInfo(@NonNull Uri pacFileUrl) {
124         mHost = LOCAL_HOST;
125         mPort = LOCAL_PORT;
126         mExclusionList = LOCAL_EXCL_LIST;
127         mParsedExclusionList = parseExclusionList(mExclusionList);
128         if (pacFileUrl == null) {
129             throw new NullPointerException();
130         }
131         mPacFileUrl = pacFileUrl;
132     }
133 
134     /**
135      * Only used in PacProxyService after Local Proxy is bound.
136      * @hide
137      */
ProxyInfo(@onNull Uri pacFileUrl, int localProxyPort)138     public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
139         mHost = LOCAL_HOST;
140         mPort = localProxyPort;
141         mExclusionList = LOCAL_EXCL_LIST;
142         mParsedExclusionList = parseExclusionList(mExclusionList);
143         if (pacFileUrl == null) {
144             throw new NullPointerException();
145         }
146         mPacFileUrl = pacFileUrl;
147     }
148 
parseExclusionList(String exclusionList)149     private static String[] parseExclusionList(String exclusionList) {
150         if (exclusionList == null) {
151             return new String[0];
152         } else {
153             return exclusionList.toLowerCase(Locale.ROOT).split(",");
154         }
155     }
156 
ProxyInfo(String host, int port, String exclList, String[] parsedExclList)157     private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
158         mHost = host;
159         mPort = port;
160         mExclusionList = exclList;
161         mParsedExclusionList = parsedExclList;
162         mPacFileUrl = Uri.EMPTY;
163     }
164 
165     /**
166      * A copy constructor to hold proxy properties.
167      */
ProxyInfo(@ullable ProxyInfo source)168     public ProxyInfo(@Nullable ProxyInfo source) {
169         if (source != null) {
170             mHost = source.getHost();
171             mPort = source.getPort();
172             mPacFileUrl = source.mPacFileUrl;
173             mExclusionList = source.getExclusionListAsString();
174             mParsedExclusionList = source.mParsedExclusionList;
175         } else {
176             mHost = null;
177             mPort = 0;
178             mExclusionList = null;
179             mParsedExclusionList = null;
180             mPacFileUrl = Uri.EMPTY;
181         }
182     }
183 
184     /**
185      * @hide
186      */
getSocketAddress()187     public InetSocketAddress getSocketAddress() {
188         InetSocketAddress inetSocketAddress = null;
189         try {
190             inetSocketAddress = new InetSocketAddress(mHost, mPort);
191         } catch (IllegalArgumentException e) { }
192         return inetSocketAddress;
193     }
194 
195     /**
196      * Returns the URL of the current PAC script or null if there is
197      * no PAC script.
198      */
getPacFileUrl()199     public Uri getPacFileUrl() {
200         return mPacFileUrl;
201     }
202 
203     /**
204      * When configured to use a Direct Proxy this returns the host
205      * of the proxy.
206      */
getHost()207     public String getHost() {
208         return mHost;
209     }
210 
211     /**
212      * When configured to use a Direct Proxy this returns the port
213      * of the proxy
214      */
getPort()215     public int getPort() {
216         return mPort;
217     }
218 
219     /**
220      * When configured to use a Direct Proxy this returns the list
221      * of hosts for which the proxy is ignored.
222      */
getExclusionList()223     public String[] getExclusionList() {
224         return mParsedExclusionList;
225     }
226 
227     /**
228      * comma separated
229      * @hide
230      */
231     @Nullable
getExclusionListAsString()232     public String getExclusionListAsString() {
233         return mExclusionList;
234     }
235 
236     /**
237      * Return true if the pattern of proxy is valid, otherwise return false.
238      */
isValid()239     public boolean isValid() {
240         if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
241         return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
242                 mPort == 0 ? "" : Integer.toString(mPort),
243                 mExclusionList == null ? "" : mExclusionList);
244     }
245 
246     /**
247      * @hide
248      */
makeProxy()249     public java.net.Proxy makeProxy() {
250         java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
251         if (mHost != null) {
252             try {
253                 InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort);
254                 proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress);
255             } catch (IllegalArgumentException e) {
256             }
257         }
258         return proxy;
259     }
260 
261     /**
262      * @hide
263      * @return whether this proxy uses a Proxy Auto Configuration URL.
264      */
isPacProxy()265     public boolean isPacProxy() {
266         return !Uri.EMPTY.equals(mPacFileUrl);
267     }
268 
269     @Override
toString()270     public String toString() {
271         StringBuilder sb = new StringBuilder();
272         if (!Uri.EMPTY.equals(mPacFileUrl)) {
273             sb.append("PAC Script: ");
274             sb.append(mPacFileUrl);
275         }
276         if (mHost != null) {
277             sb.append("[");
278             sb.append(mHost);
279             sb.append("] ");
280             sb.append(Integer.toString(mPort));
281             if (mExclusionList != null) {
282                 sb.append(" xl=").append(mExclusionList);
283             }
284         } else {
285             sb.append("[ProxyProperties.mHost == null]");
286         }
287         return sb.toString();
288     }
289 
290     @Override
equals(@ullable Object o)291     public boolean equals(@Nullable Object o) {
292         if (!(o instanceof ProxyInfo)) return false;
293         ProxyInfo p = (ProxyInfo)o;
294         // If PAC URL is present in either then they must be equal.
295         // Other parameters will only be for fall back.
296         if (!Uri.EMPTY.equals(mPacFileUrl)) {
297             return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
298         }
299         if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
300             return false;
301         }
302         if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
303             return false;
304         }
305         if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
306             return false;
307         }
308         if (mHost != null && p.mHost == null) return false;
309         if (mHost == null && p.mHost != null) return false;
310         if (mPort != p.mPort) return false;
311         return true;
312     }
313 
314     /**
315      * Implement the Parcelable interface
316      * @hide
317      */
describeContents()318     public int describeContents() {
319         return 0;
320     }
321 
322     @Override
323     /*
324      * generate hashcode based on significant fields
325      */
hashCode()326     public int hashCode() {
327         return ((null == mHost) ? 0 : mHost.hashCode())
328                 + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
329                 + mPort;
330     }
331 
332     /**
333      * Implement the Parcelable interface.
334      * @hide
335      */
writeToParcel(Parcel dest, int flags)336     public void writeToParcel(Parcel dest, int flags) {
337         if (!Uri.EMPTY.equals(mPacFileUrl)) {
338             dest.writeByte((byte)1);
339             mPacFileUrl.writeToParcel(dest, 0);
340             dest.writeInt(mPort);
341             return;
342         } else {
343             dest.writeByte((byte)0);
344         }
345         if (mHost != null) {
346             dest.writeByte((byte)1);
347             dest.writeString(mHost);
348             dest.writeInt(mPort);
349         } else {
350             dest.writeByte((byte)0);
351         }
352         dest.writeString(mExclusionList);
353         dest.writeStringArray(mParsedExclusionList);
354     }
355 
356     public static final @android.annotation.NonNull Creator<ProxyInfo> CREATOR =
357         new Creator<ProxyInfo>() {
358             public ProxyInfo createFromParcel(Parcel in) {
359                 String host = null;
360                 int port = 0;
361                 if (in.readByte() != 0) {
362                     Uri url = Uri.CREATOR.createFromParcel(in);
363                     int localPort = in.readInt();
364                     return new ProxyInfo(url, localPort);
365                 }
366                 if (in.readByte() != 0) {
367                     host = in.readString();
368                     port = in.readInt();
369                 }
370                 String exclList = in.readString();
371                 String[] parsedExclList = in.createStringArray();
372                 ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
373                 return proxyProperties;
374             }
375 
376             public ProxyInfo[] newArray(int size) {
377                 return new ProxyInfo[size];
378             }
379         };
380 }
381