blob: e715890fb211d437205f9ab04cd7372c99005b61 [file] [log] [blame]
Chalard Jean52c2aa72018-06-07 16:44:04 +09001/**
2 * Copyright (c) 2018, 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 com.android.server.connectivity;
18
Chalard Jean1b309832018-06-08 19:32:17 +090019import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST;
20import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST;
21import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC;
22import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT;
23import static android.provider.Settings.Global.HTTP_PROXY;
24
Chalard Jean52c2aa72018-06-07 16:44:04 +090025import android.annotation.NonNull;
26import android.annotation.Nullable;
Chalard Jean3c443fc2018-06-07 18:37:59 +090027import android.content.ContentResolver;
Chalard Jeanff5d0052018-06-07 19:20:08 +090028import android.content.Context;
Chalard Jean3319c622018-06-07 19:30:29 +090029import android.content.Intent;
30import android.net.Proxy;
Chalard Jean52c2aa72018-06-07 16:44:04 +090031import android.net.ProxyInfo;
Chalard Jean4949dd72018-06-07 17:41:29 +090032import android.net.Uri;
Chalard Jean3319c622018-06-07 19:30:29 +090033import android.os.Binder;
Chalard Jeanff5d0052018-06-07 19:20:08 +090034import android.os.Handler;
Chalard Jean3319c622018-06-07 19:30:29 +090035import android.os.UserHandle;
Chalard Jean3c443fc2018-06-07 18:37:59 +090036import android.provider.Settings;
Chalard Jean4949dd72018-06-07 17:41:29 +090037import android.text.TextUtils;
Chalard Jean3319c622018-06-07 19:30:29 +090038import android.util.Slog;
Chalard Jean52c2aa72018-06-07 16:44:04 +090039
40import com.android.internal.annotations.GuardedBy;
41
Chalard Jean4949dd72018-06-07 17:41:29 +090042import java.util.Objects;
43
Chalard Jean52c2aa72018-06-07 16:44:04 +090044/**
45 * A class to handle proxy for ConnectivityService.
46 *
47 * @hide
48 */
49public class ProxyTracker {
Chalard Jean3319c622018-06-07 19:30:29 +090050 private static final String TAG = ProxyTracker.class.getSimpleName();
51 private static final boolean DBG = true;
52
53 @NonNull
54 private final Context mContext;
55
Chalard Jean52c2aa72018-06-07 16:44:04 +090056 @NonNull
Chalard Jean1b309832018-06-08 19:32:17 +090057 private final Object mProxyLock = new Object();
Chalard Jeand82ba6d2018-06-08 12:41:21 +090058 // The global proxy is the proxy that is set device-wide, overriding any network-specific
59 // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence
60 // this value is only for querying.
Chalard Jean52c2aa72018-06-07 16:44:04 +090061 @Nullable
62 @GuardedBy("mProxyLock")
Chalard Jean1b309832018-06-08 19:32:17 +090063 private ProxyInfo mGlobalProxy = null;
Chalard Jeand82ba6d2018-06-08 12:41:21 +090064 // The default proxy is the proxy that applies to no particular network if the global proxy
65 // is not set. Individual networks have their own settings that override this. This member
66 // is set through setDefaultProxy, which is called when the default network changes proxies
67 // in its LinkProperties, or when ConnectivityService switches to a new default network, or
68 // when PacManager resolves the proxy.
Chalard Jean52c2aa72018-06-07 16:44:04 +090069 @Nullable
70 @GuardedBy("mProxyLock")
Chalard Jean1b309832018-06-08 19:32:17 +090071 private volatile ProxyInfo mDefaultProxy = null;
72 // Whether the default proxy is enabled.
Chalard Jean52c2aa72018-06-07 16:44:04 +090073 @GuardedBy("mProxyLock")
Chalard Jean1b309832018-06-08 19:32:17 +090074 private boolean mDefaultProxyEnabled = true;
Chalard Jean4949dd72018-06-07 17:41:29 +090075
Chalard Jeand82ba6d2018-06-08 12:41:21 +090076 // The object responsible for Proxy Auto Configuration (PAC).
Chalard Jeanff5d0052018-06-07 19:20:08 +090077 @NonNull
78 private final PacManager mPacManager;
79
80 public ProxyTracker(@NonNull final Context context,
81 @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
Chalard Jean3319c622018-06-07 19:30:29 +090082 mContext = context;
Chalard Jeanff5d0052018-06-07 19:20:08 +090083 mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
84 }
85
Chalard Jean4949dd72018-06-07 17:41:29 +090086 // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
87 // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
88 // proxy is null then there is no proxy in place).
89 @Nullable
90 private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) {
91 if (proxy != null && TextUtils.isEmpty(proxy.getHost())
Chalard Jean1b309832018-06-08 19:32:17 +090092 && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Chalard Jean4949dd72018-06-07 17:41:29 +090093 return null;
94 }
95 return proxy;
96 }
97
98 // ProxyInfo equality functions with a couple modifications over ProxyInfo.equals() to make it
99 // better for determining if a new proxy broadcast is necessary:
100 // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
101 // avoid unnecessary broadcasts.
102 // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
103 // is in place. This is important so legacy PAC resolver (see com.android.proxyhandler)
104 // changes aren't missed. The legacy PAC resolver pretends to be a simple HTTP proxy but
105 // actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
106 // all set.
107 public static boolean proxyInfoEqual(@Nullable final ProxyInfo a, @Nullable final ProxyInfo b) {
108 final ProxyInfo pa = canonicalizeProxyInfo(a);
109 final ProxyInfo pb = canonicalizeProxyInfo(b);
110 // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
111 // hosts even when PAC URLs are present to account for the legacy PAC resolver.
112 return Objects.equals(pa, pb) && (pa == null || Objects.equals(pa.getHost(), pb.getHost()));
113 }
Chalard Jean5afbc832018-06-07 18:02:37 +0900114
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900115 /**
116 * Gets the default system-wide proxy.
117 *
118 * This will return the global proxy if set, otherwise the default proxy if in use. Note
119 * that this is not necessarily the proxy that any given process should use, as the right
120 * proxy for a process is the proxy for the network this process will use, which may be
121 * different from this value. This value is simply the default in case there is no proxy set
122 * in the network that will be used by a specific process.
123 * @return The default system-wide proxy or null if none.
124 */
Chalard Jean5afbc832018-06-07 18:02:37 +0900125 @Nullable
126 public ProxyInfo getDefaultProxy() {
127 // This information is already available as a world read/writable jvm property.
128 synchronized (mProxyLock) {
Chalard Jean19aa80f2018-06-08 19:39:24 +0900129 if (mGlobalProxy != null) return mGlobalProxy;
130 if (mDefaultProxyEnabled) return mDefaultProxy;
131 return null;
Chalard Jean5afbc832018-06-07 18:02:37 +0900132 }
133 }
134
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900135 /**
136 * Gets the global proxy.
137 *
138 * @return The global proxy or null if none.
139 */
Chalard Jean5afbc832018-06-07 18:02:37 +0900140 @Nullable
141 public ProxyInfo getGlobalProxy() {
142 // This information is already available as a world read/writable jvm property.
143 synchronized (mProxyLock) {
144 return mGlobalProxy;
145 }
146 }
Chalard Jeanff5d0052018-06-07 19:20:08 +0900147
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900148 /**
Chalard Jean655ad152018-06-08 12:47:42 +0900149 * Read the global proxy settings and cache them in memory.
150 */
151 public void loadGlobalProxy() {
152 ContentResolver res = mContext.getContentResolver();
Chalard Jean1b309832018-06-08 19:32:17 +0900153 String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST);
154 int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0);
155 String exclList = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
156 String pacFileUrl = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_PAC);
Chalard Jean655ad152018-06-08 12:47:42 +0900157 if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
158 ProxyInfo proxyProperties;
159 if (!TextUtils.isEmpty(pacFileUrl)) {
160 proxyProperties = new ProxyInfo(pacFileUrl);
161 } else {
162 proxyProperties = new ProxyInfo(host, port, exclList);
163 }
164 if (!proxyProperties.isValid()) {
165 if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
166 return;
167 }
168
169 synchronized (mProxyLock) {
170 mGlobalProxy = proxyProperties;
171 }
172 }
Chalard Jeanb9d45fd2018-06-08 14:24:49 +0900173 loadDeprecatedGlobalHttpProxy();
Chalard Jean655ad152018-06-08 12:47:42 +0900174 // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
175 }
176
177 /**
Chalard Jeanb9d45fd2018-06-08 14:24:49 +0900178 * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it.
179 */
180 public void loadDeprecatedGlobalHttpProxy() {
Chalard Jean1b309832018-06-08 19:32:17 +0900181 final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY);
Chalard Jeanb9d45fd2018-06-08 14:24:49 +0900182 if (!TextUtils.isEmpty(proxy)) {
183 String data[] = proxy.split(":");
184 if (data.length == 0) {
185 return;
186 }
187
188 final String proxyHost = data[0];
189 int proxyPort = 8080;
190 if (data.length > 1) {
191 try {
192 proxyPort = Integer.parseInt(data[1]);
193 } catch (NumberFormatException e) {
194 return;
195 }
196 }
197 final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
198 setGlobalProxy(p);
199 }
200 }
201
202 /**
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900203 * Sends the system broadcast informing apps about a new proxy configuration.
204 *
205 * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
206 * to do in a "sendProxyBroadcast" method.
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900207 */
Chalard Jeanb50a2762018-06-08 19:46:44 +0900208 public void sendProxyBroadcast() {
209 final ProxyInfo defaultProxy = getDefaultProxy();
210 final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
Chalard Jean29f63af2018-10-15 14:27:15 +0900211 if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
Chalard Jean69452312018-06-11 16:58:52 +0900212 return;
213 }
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900214 if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
Chalard Jean3319c622018-06-07 19:30:29 +0900215 Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
216 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
217 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900218 intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
Chalard Jean3319c622018-06-07 19:30:29 +0900219 final long ident = Binder.clearCallingIdentity();
220 try {
221 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
222 } finally {
223 Binder.restoreCallingIdentity(ident);
224 }
225 }
Chalard Jean3c443fc2018-06-07 18:37:59 +0900226
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900227 /**
228 * Sets the global proxy in memory. Also writes the values to the global settings of the device.
229 *
230 * @param proxyInfo the proxy spec, or null for no proxy.
231 */
232 public void setGlobalProxy(@Nullable ProxyInfo proxyInfo) {
Chalard Jean3c443fc2018-06-07 18:37:59 +0900233 synchronized (mProxyLock) {
Chalard Jean1900b592018-06-07 19:40:24 +0900234 // ProxyInfo#equals is not commutative :( and is public API, so it can't be fixed.
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900235 if (proxyInfo == mGlobalProxy) return;
236 if (proxyInfo != null && proxyInfo.equals(mGlobalProxy)) return;
237 if (mGlobalProxy != null && mGlobalProxy.equals(proxyInfo)) return;
Chalard Jean3c443fc2018-06-07 18:37:59 +0900238
Chalard Jean1900b592018-06-07 19:40:24 +0900239 final String host;
240 final int port;
241 final String exclList;
242 final String pacFileUrl;
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900243 if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
244 !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
245 if (!proxyInfo.isValid()) {
246 if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
Chalard Jean3c443fc2018-06-07 18:37:59 +0900247 return;
248 }
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900249 mGlobalProxy = new ProxyInfo(proxyInfo);
Chalard Jean3c443fc2018-06-07 18:37:59 +0900250 host = mGlobalProxy.getHost();
251 port = mGlobalProxy.getPort();
252 exclList = mGlobalProxy.getExclusionListAsString();
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900253 pacFileUrl = Uri.EMPTY.equals(proxyInfo.getPacFileUrl())
254 ? "" : proxyInfo.getPacFileUrl().toString();
Chalard Jean3c443fc2018-06-07 18:37:59 +0900255 } else {
Chalard Jean1900b592018-06-07 19:40:24 +0900256 host = "";
257 port = 0;
258 exclList = "";
259 pacFileUrl = "";
Chalard Jean3c443fc2018-06-07 18:37:59 +0900260 mGlobalProxy = null;
261 }
262 final ContentResolver res = mContext.getContentResolver();
263 final long token = Binder.clearCallingIdentity();
264 try {
Chalard Jean1b309832018-06-08 19:32:17 +0900265 Settings.Global.putString(res, GLOBAL_HTTP_PROXY_HOST, host);
266 Settings.Global.putInt(res, GLOBAL_HTTP_PROXY_PORT, port);
267 Settings.Global.putString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclList);
268 Settings.Global.putString(res, GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
Chalard Jean3c443fc2018-06-07 18:37:59 +0900269 } finally {
270 Binder.restoreCallingIdentity(token);
271 }
272
Chalard Jeanb50a2762018-06-08 19:46:44 +0900273 sendProxyBroadcast();
Chalard Jean3c443fc2018-06-07 18:37:59 +0900274 }
275 }
Chalard Jeandaab8f92018-06-08 12:20:15 +0900276
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900277 /**
278 * Sets the default proxy for the device.
279 *
280 * The default proxy is the proxy used for networks that do not have a specific proxy.
281 * @param proxyInfo the proxy spec, or null for no proxy.
282 */
283 public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
Chalard Jeandaab8f92018-06-08 12:20:15 +0900284 synchronized (mProxyLock) {
Chalard Jean1b309832018-06-08 19:32:17 +0900285 if (Objects.equals(mDefaultProxy, proxyInfo)) return;
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900286 if (proxyInfo != null && !proxyInfo.isValid()) {
287 if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
Chalard Jeandaab8f92018-06-08 12:20:15 +0900288 return;
289 }
290
291 // This call could be coming from the PacManager, containing the port of the local
292 // proxy. If this new proxy matches the global proxy then copy this proxy to the
293 // global (to get the correct local port), and send a broadcast.
294 // TODO: Switch PacManager to have its own message to send back rather than
295 // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900296 if ((mGlobalProxy != null) && (proxyInfo != null)
297 && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
298 && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
299 mGlobalProxy = proxyInfo;
Chalard Jeanb50a2762018-06-08 19:46:44 +0900300 sendProxyBroadcast();
Chalard Jeandaab8f92018-06-08 12:20:15 +0900301 return;
302 }
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900303 mDefaultProxy = proxyInfo;
Chalard Jeandaab8f92018-06-08 12:20:15 +0900304
305 if (mGlobalProxy != null) return;
Chalard Jean1b309832018-06-08 19:32:17 +0900306 if (mDefaultProxyEnabled) {
Chalard Jeanb50a2762018-06-08 19:46:44 +0900307 sendProxyBroadcast();
Chalard Jeandaab8f92018-06-08 12:20:15 +0900308 }
309 }
310 }
Chalard Jean52c2aa72018-06-07 16:44:04 +0900311}