blob: 621c9c71df58c4305567af324457e43a22b6de5f [file] [log] [blame]
Jason Monk9ced3cd2013-08-12 16:42:38 -04001/**
2 * Copyright (c) 2013, 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 */
Jason Monk602b2322013-07-03 17:04:33 -040016package com.android.server.connectivity;
17
Daichi Hirono4010fe42017-03-17 09:12:12 +090018import android.annotation.WorkerThread;
Jason Monk602b2322013-07-03 17:04:33 -040019import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
Jason Monk9ced3cd2013-08-12 16:42:38 -040022import android.content.ComponentName;
Jason Monk602b2322013-07-03 17:04:33 -040023import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Jason Monk9ced3cd2013-08-12 16:42:38 -040027import android.content.ServiceConnection;
Jason Monk207900c2014-04-25 15:00:09 -040028import android.net.ProxyInfo;
Jeff Sharkey619a5112017-01-19 11:55:54 -070029import android.net.TrafficStats;
Jason Monk83520b92014-05-09 15:16:06 -040030import android.net.Uri;
Jason Monkdecd2952013-10-10 14:02:51 -040031import android.os.Handler;
Paul Jensen7d2198b2016-08-22 09:15:40 -040032import android.os.HandlerThread;
Jason Monk9ced3cd2013-08-12 16:42:38 -040033import android.os.IBinder;
Jason Monk602b2322013-07-03 17:04:33 -040034import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.os.SystemClock;
37import android.os.SystemProperties;
38import android.provider.Settings;
Jason Monk602b2322013-07-03 17:04:33 -040039import android.util.Log;
chiachangwangba482c62022-06-02 10:22:20 +000040import android.webkit.URLUtil;
Jason Monk602b2322013-07-03 17:04:33 -040041
Jason Monk9ced3cd2013-08-12 16:42:38 -040042import com.android.internal.annotations.GuardedBy;
Chalard Jean6a76b7e2019-04-09 11:16:56 +090043import com.android.internal.util.TrafficStatsConstants;
Jason Monk6f8a68f2013-08-23 19:21:25 -040044import com.android.net.IProxyCallback;
45import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040046import com.android.net.IProxyService;
47
Paul Jensen7d2198b2016-08-22 09:15:40 -040048import java.io.ByteArrayOutputStream;
Jason Monk602b2322013-07-03 17:04:33 -040049import java.io.IOException;
Jason Monk602b2322013-07-03 17:04:33 -040050import java.net.URL;
51import java.net.URLConnection;
52
53/**
54 * @hide
55 */
Jason Monk9ced3cd2013-08-12 16:42:38 -040056public class PacManager {
Chalard Jean918a68b2018-01-19 17:00:47 +090057 private static final String PAC_PACKAGE = "com.android.pacprocessor";
58 private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
59 private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040060
Chalard Jean918a68b2018-01-19 17:00:47 +090061 private static final String PROXY_PACKAGE = "com.android.proxyhandler";
62 private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040063
Jason Monk9ced3cd2013-08-12 16:42:38 -040064 private static final String TAG = "PacManager";
Jason Monk602b2322013-07-03 17:04:33 -040065
66 private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
67
68 private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
69 private static final int DELAY_1 = 0;
70 private static final int DELAY_4 = 3;
71 private static final int DELAY_LONG = 4;
Paul Jensen7d2198b2016-08-22 09:15:40 -040072 private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
Jason Monk602b2322013-07-03 17:04:33 -040073
Chalard Jean69452312018-06-11 16:58:52 +090074 // Return values for #setCurrentProxyScriptUrl
Chalard Jean29f63af2018-10-15 14:27:15 +090075 public static final boolean DONT_SEND_BROADCAST = false;
76 public static final boolean DO_SEND_BROADCAST = true;
Chalard Jean69452312018-06-11 16:58:52 +090077
Jason Monk602b2322013-07-03 17:04:33 -040078 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040079 @GuardedBy("mProxyLock")
Daichi Hirono4010fe42017-03-17 09:12:12 +090080 private volatile Uri mPacUrl = Uri.EMPTY;
Jason Monk602b2322013-07-03 17:04:33 -040081
82 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040083 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040084 private IProxyService mProxyService;
85 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040086 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040087 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040088 private Context mContext;
89
90 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040091 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040092
Daichi Hirono4010fe42017-03-17 09:12:12 +090093 private volatile boolean mHasSentBroadcast;
94 private volatile boolean mHasDownloaded;
Jason Monkd4434792013-09-23 13:32:39 -040095
Jason Monkdecd2952013-10-10 14:02:51 -040096 private Handler mConnectivityHandler;
Chalard Jean3c443fc2018-06-07 18:37:59 +090097 private final int mProxyMessage;
Jason Monkdecd2952013-10-10 14:02:51 -040098
Jason Monk9ced3cd2013-08-12 16:42:38 -040099 /**
Daichi Hirono4010fe42017-03-17 09:12:12 +0900100 * Used for locking when setting mProxyService and all references to mCurrentPac.
Jason Monk9ced3cd2013-08-12 16:42:38 -0400101 */
102 private final Object mProxyLock = new Object();
103
Daichi Hirono4010fe42017-03-17 09:12:12 +0900104 /**
105 * Runnable to download PAC script.
Chalard Jean3c443fc2018-06-07 18:37:59 +0900106 * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
Daichi Hirono4010fe42017-03-17 09:12:12 +0900107 * latest data fetched from mPacUrl is stored in mProxyService.
108 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400109 private Runnable mPacDownloader = new Runnable() {
110 @Override
Daichi Hirono4010fe42017-03-17 09:12:12 +0900111 @WorkerThread
Jason Monk9ced3cd2013-08-12 16:42:38 -0400112 public void run() {
113 String file;
Daichi Hirono4010fe42017-03-17 09:12:12 +0900114 final Uri pacUrl = mPacUrl;
115 if (Uri.EMPTY.equals(pacUrl)) return;
Chalard Jean6a76b7e2019-04-09 11:16:56 +0900116 final int oldTag = TrafficStats.getAndSetThreadStatsTag(
117 TrafficStatsConstants.TAG_SYSTEM_PAC);
Daichi Hirono4010fe42017-03-17 09:12:12 +0900118 try {
119 file = get(pacUrl);
120 } catch (IOException ioe) {
121 file = null;
122 Log.w(TAG, "Failed to load PAC file: " + ioe);
123 } finally {
124 TrafficStats.setThreadStatsTag(oldTag);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400125 }
126 if (file != null) {
127 synchronized (mProxyLock) {
128 if (!file.equals(mCurrentPac)) {
129 setCurrentProxyScript(file);
130 }
131 }
Jason Monkd4434792013-09-23 13:32:39 -0400132 mHasDownloaded = true;
133 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400134 longSchedule();
135 } else {
136 reschedule();
137 }
138 }
139 };
140
Paul Jensen7d2198b2016-08-22 09:15:40 -0400141 private final Handler mNetThreadHandler;
142
Jason Monk602b2322013-07-03 17:04:33 -0400143 class PacRefreshIntentReceiver extends BroadcastReceiver {
144 public void onReceive(Context context, Intent intent) {
Paul Jensen7d2198b2016-08-22 09:15:40 -0400145 mNetThreadHandler.post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400146 }
147 }
148
Jason Monkdecd2952013-10-10 14:02:51 -0400149 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400150 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400151 mLastPort = -1;
Chalard Jean3c443fc2018-06-07 18:37:59 +0900152 final HandlerThread netThread = new HandlerThread("android.pacmanager",
153 android.os.Process.THREAD_PRIORITY_DEFAULT);
154 netThread.start();
155 mNetThreadHandler = new Handler(netThread.getLooper());
Jason Monk602b2322013-07-03 17:04:33 -0400156
157 mPacRefreshIntent = PendingIntent.getBroadcast(
158 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
159 context.registerReceiver(new PacRefreshIntentReceiver(),
160 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400161 mConnectivityHandler = handler;
162 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400163 }
164
165 private AlarmManager getAlarmManager() {
166 if (mAlarmManager == null) {
167 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
168 }
169 return mAlarmManager;
170 }
171
Jason Monk6f8a68f2013-08-23 19:21:25 -0400172 /**
173 * Updates the PAC Manager with current Proxy information. This is called by
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900174 * the ProxyTracker directly before a broadcast takes place to allow
Jason Monk6f8a68f2013-08-23 19:21:25 -0400175 * the PacManager to indicate that the broadcast should not be sent and the
176 * PacManager will trigger a new broadcast when it is ready.
177 *
178 * @param proxy Proxy information that is about to be broadcast.
Chalard Jean69452312018-06-11 16:58:52 +0900179 * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
Jason Monk6f8a68f2013-08-23 19:21:25 -0400180 */
Chalard Jean29f63af2018-10-15 14:27:15 +0900181 synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
Jason Monk83520b92014-05-09 15:16:06 -0400182 if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400183 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400184 // Allow to send broadcast, nothing to do.
Chalard Jean29f63af2018-10-15 14:27:15 +0900185 return DO_SEND_BROADCAST;
Jason Monkdecd2952013-10-10 14:02:51 -0400186 }
Daichi Hirono4010fe42017-03-17 09:12:12 +0900187 mPacUrl = proxy.getPacFileUrl();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400188 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400189 mHasSentBroadcast = false;
190 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400191 getAlarmManager().cancel(mPacRefreshIntent);
192 bind();
Chalard Jean29f63af2018-10-15 14:27:15 +0900193 return DONT_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400194 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400195 getAlarmManager().cancel(mPacRefreshIntent);
196 synchronized (mProxyLock) {
Jason Monkf6b46cb2014-09-19 12:46:19 -0400197 mPacUrl = Uri.EMPTY;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400198 mCurrentPac = null;
199 if (mProxyService != null) {
200 try {
201 mProxyService.stopPacSystem();
202 } catch (RemoteException e) {
203 Log.w(TAG, "Failed to stop PAC service", e);
204 } finally {
205 unbind();
206 }
207 }
Jason Monk602b2322013-07-03 17:04:33 -0400208 }
Chalard Jean29f63af2018-10-15 14:27:15 +0900209 return DO_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400210 }
211 }
212
213 /**
214 * Does a post and reports back the status code.
215 *
Chalard Jean3c443fc2018-06-07 18:37:59 +0900216 * @throws IOException if the URL is malformed, or the PAC file is too big.
Jason Monk602b2322013-07-03 17:04:33 -0400217 */
Jason Monk83520b92014-05-09 15:16:06 -0400218 private static String get(Uri pacUri) throws IOException {
chiachangwangba482c62022-06-02 10:22:20 +0000219 if (!URLUtil.isValidUrl(pacUri.toString())) {
220 throw new IOException("Malformed URL:" + pacUri);
221 }
222
223 final URL url = new URL(pacUri.toString());
224 URLConnection urlConnection;
225 try {
226 urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
227 // Catch the possible exceptions and rethrow as IOException to not to crash the system
228 // for illegal input.
229 } catch (IllegalArgumentException e) {
230 throw new IOException("Incorrect proxy type for " + pacUri);
231 } catch (UnsupportedOperationException e) {
232 throw new IOException("Unsupported URL connection type for " + pacUri);
233 }
234
Paul Jensen7d2198b2016-08-22 09:15:40 -0400235 long contentLength = -1;
236 try {
237 contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
238 } catch (NumberFormatException e) {
239 // Ignore
240 }
241 if (contentLength > MAX_PAC_SIZE) {
242 throw new IOException("PAC too big: " + contentLength + " bytes");
243 }
244 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
245 byte[] buffer = new byte[1024];
246 int count;
247 while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
248 bytes.write(buffer, 0, count);
249 if (bytes.size() > MAX_PAC_SIZE) {
250 throw new IOException("PAC too big");
251 }
252 }
253 return bytes.toString();
Jason Monk602b2322013-07-03 17:04:33 -0400254 }
255
256 private int getNextDelay(int currentDelay) {
257 if (++currentDelay > DELAY_4) {
258 return DELAY_4;
259 }
260 return currentDelay;
261 }
262
263 private void longSchedule() {
264 mCurrentDelay = DELAY_1;
265 setDownloadIn(DELAY_LONG);
266 }
267
268 private void reschedule() {
269 mCurrentDelay = getNextDelay(mCurrentDelay);
270 setDownloadIn(mCurrentDelay);
271 }
272
273 private String getPacChangeDelay() {
274 final ContentResolver cr = mContext.getContentResolver();
275
Chalard Jean3c443fc2018-06-07 18:37:59 +0900276 // Check system properties for the default value then use secure settings value, if any.
Jason Monk602b2322013-07-03 17:04:33 -0400277 String defaultDelay = SystemProperties.get(
278 "conn." + Settings.Global.PAC_CHANGE_DELAY,
279 DEFAULT_DELAYS);
280 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
281 return (val == null) ? defaultDelay : val;
282 }
283
284 private long getDownloadDelay(int delayIndex) {
285 String[] list = getPacChangeDelay().split(" ");
286 if (delayIndex < list.length) {
287 return Long.parseLong(list[delayIndex]);
288 }
289 return 0;
290 }
291
292 private void setDownloadIn(int delayIndex) {
293 long delay = getDownloadDelay(delayIndex);
294 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
295 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
296 }
297
Chalard Jean3c443fc2018-06-07 18:37:59 +0900298 private void setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700299 if (mProxyService == null) {
300 Log.e(TAG, "setCurrentProxyScript: no proxy service");
Chiachang Wangc517a6f2019-01-17 10:18:39 +0800301 return;
Wink Savillea48ad8b2013-08-10 11:22:31 -0700302 }
Jason Monk602b2322013-07-03 17:04:33 -0400303 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400304 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400305 mCurrentPac = script;
306 } catch (RemoteException e) {
307 Log.e(TAG, "Unable to set PAC file", e);
308 }
Jason Monk602b2322013-07-03 17:04:33 -0400309 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400310
311 private void bind() {
312 if (mContext == null) {
313 Log.e(TAG, "No context for binding");
314 return;
315 }
316 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400317 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monkbc018d82013-09-17 16:37:38 -0400318 if ((mProxyConnection != null) && (mConnection != null)) {
Chalard Jean69452312018-06-11 16:58:52 +0900319 // Already bound: no need to bind again, just download the new file.
Paul Jensenbaae57a2016-09-19 08:53:20 -0400320 mNetThreadHandler.post(mPacDownloader);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400321 return;
322 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400323 mConnection = new ServiceConnection() {
324 @Override
325 public void onServiceDisconnected(ComponentName component) {
326 synchronized (mProxyLock) {
327 mProxyService = null;
328 }
329 }
330
331 @Override
332 public void onServiceConnected(ComponentName component, IBinder binder) {
333 synchronized (mProxyLock) {
334 try {
Jason Monkda205a72013-08-21 15:57:30 -0400335 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400336 + binder.getInterfaceDescriptor());
337 } catch (RemoteException e1) {
338 Log.e(TAG, "Remote Exception", e1);
339 }
Jason Monkda205a72013-08-21 15:57:30 -0400340 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400341 mProxyService = IProxyService.Stub.asInterface(binder);
342 if (mProxyService == null) {
343 Log.e(TAG, "No proxy service");
344 } else {
345 try {
346 mProxyService.startPacSystem();
347 } catch (RemoteException e) {
348 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
349 }
Paul Jensen7d2198b2016-08-22 09:15:40 -0400350 mNetThreadHandler.post(mPacDownloader);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400351 }
352 }
353 }
354 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400355 mContext.bindService(intent, mConnection,
356 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400357
358 intent = new Intent();
359 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
360 mProxyConnection = new ServiceConnection() {
361 @Override
362 public void onServiceDisconnected(ComponentName component) {
363 }
364
365 @Override
366 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400367 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
368 if (callbackService != null) {
369 try {
370 callbackService.getProxyPort(new IProxyPortListener.Stub() {
371 @Override
Chalard Jean3c443fc2018-06-07 18:37:59 +0900372 public void setProxyPort(int port) {
Jason Monkd4434792013-09-23 13:32:39 -0400373 if (mLastPort != -1) {
374 // Always need to send if port changed
375 mHasSentBroadcast = false;
376 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400377 mLastPort = port;
378 if (port != -1) {
379 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400380 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400381 } else {
382 Log.e(TAG, "Received invalid port from Local Proxy,"
383 + " PAC will not be operational");
384 }
385 }
386 });
387 } catch (RemoteException e) {
388 e.printStackTrace();
389 }
390 }
Jason Monkda205a72013-08-21 15:57:30 -0400391 }
392 };
393 mContext.bindService(intent, mProxyConnection,
394 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400395 }
396
397 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400398 if (mConnection != null) {
399 mContext.unbindService(mConnection);
400 mConnection = null;
401 }
402 if (mProxyConnection != null) {
403 mContext.unbindService(mProxyConnection);
404 mProxyConnection = null;
405 }
406 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400407 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400408 }
409
Jason Monk207900c2014-04-25 15:00:09 -0400410 private void sendPacBroadcast(ProxyInfo proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400411 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400412 }
Jason Monkd4434792013-09-23 13:32:39 -0400413
414 private synchronized void sendProxyIfNeeded() {
415 if (!mHasDownloaded || (mLastPort == -1)) {
416 return;
417 }
418 if (!mHasSentBroadcast) {
Jason Monk207900c2014-04-25 15:00:09 -0400419 sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
Jason Monkd4434792013-09-23 13:32:39 -0400420 mHasSentBroadcast = true;
421 }
422 }
Jason Monk602b2322013-07-03 17:04:33 -0400423}