blob: 97896889f24300ba8f75c9e244b0016d7b92a6e5 [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;
40
Jason Monk9ced3cd2013-08-12 16:42:38 -040041import com.android.internal.annotations.GuardedBy;
Jason Monk6f8a68f2013-08-23 19:21:25 -040042import com.android.net.IProxyCallback;
43import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040044import com.android.net.IProxyService;
45
Paul Jensen7d2198b2016-08-22 09:15:40 -040046import java.io.ByteArrayOutputStream;
Jason Monk602b2322013-07-03 17:04:33 -040047import java.io.IOException;
Jason Monk602b2322013-07-03 17:04:33 -040048import java.net.URL;
49import java.net.URLConnection;
50
51/**
52 * @hide
53 */
Jason Monk9ced3cd2013-08-12 16:42:38 -040054public class PacManager {
Chalard Jean918a68b2018-01-19 17:00:47 +090055 private static final String PAC_PACKAGE = "com.android.pacprocessor";
56 private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
57 private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040058
Chalard Jean918a68b2018-01-19 17:00:47 +090059 private static final String PROXY_PACKAGE = "com.android.proxyhandler";
60 private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040061
Jason Monk9ced3cd2013-08-12 16:42:38 -040062 private static final String TAG = "PacManager";
Jason Monk602b2322013-07-03 17:04:33 -040063
64 private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
65
66 private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
67 private static final int DELAY_1 = 0;
68 private static final int DELAY_4 = 3;
69 private static final int DELAY_LONG = 4;
Paul Jensen7d2198b2016-08-22 09:15:40 -040070 private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
Jason Monk602b2322013-07-03 17:04:33 -040071
Chalard Jean69452312018-06-11 16:58:52 +090072 // Return values for #setCurrentProxyScriptUrl
73 enum ToSendOrNotToSendBroadcast {
74 DONT_SEND_BROADCAST, DO_SEND_BROADCAST
75 }
76
Jason Monk602b2322013-07-03 17:04:33 -040077 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040078 @GuardedBy("mProxyLock")
Daichi Hirono4010fe42017-03-17 09:12:12 +090079 private volatile Uri mPacUrl = Uri.EMPTY;
Jason Monk602b2322013-07-03 17:04:33 -040080
81 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040082 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040083 private IProxyService mProxyService;
84 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040085 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040086 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040087 private Context mContext;
88
89 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040090 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040091
Daichi Hirono4010fe42017-03-17 09:12:12 +090092 private volatile boolean mHasSentBroadcast;
93 private volatile boolean mHasDownloaded;
Jason Monkd4434792013-09-23 13:32:39 -040094
Jason Monkdecd2952013-10-10 14:02:51 -040095 private Handler mConnectivityHandler;
Chalard Jean3c443fc2018-06-07 18:37:59 +090096 private final int mProxyMessage;
Jason Monkdecd2952013-10-10 14:02:51 -040097
Jason Monk9ced3cd2013-08-12 16:42:38 -040098 /**
Daichi Hirono4010fe42017-03-17 09:12:12 +090099 * Used for locking when setting mProxyService and all references to mCurrentPac.
Jason Monk9ced3cd2013-08-12 16:42:38 -0400100 */
101 private final Object mProxyLock = new Object();
102
Daichi Hirono4010fe42017-03-17 09:12:12 +0900103 /**
104 * Runnable to download PAC script.
Chalard Jean3c443fc2018-06-07 18:37:59 +0900105 * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
Daichi Hirono4010fe42017-03-17 09:12:12 +0900106 * latest data fetched from mPacUrl is stored in mProxyService.
107 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400108 private Runnable mPacDownloader = new Runnable() {
109 @Override
Daichi Hirono4010fe42017-03-17 09:12:12 +0900110 @WorkerThread
Jason Monk9ced3cd2013-08-12 16:42:38 -0400111 public void run() {
112 String file;
Daichi Hirono4010fe42017-03-17 09:12:12 +0900113 final Uri pacUrl = mPacUrl;
114 if (Uri.EMPTY.equals(pacUrl)) return;
115 final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
116 try {
117 file = get(pacUrl);
118 } catch (IOException ioe) {
119 file = null;
120 Log.w(TAG, "Failed to load PAC file: " + ioe);
121 } finally {
122 TrafficStats.setThreadStatsTag(oldTag);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400123 }
124 if (file != null) {
125 synchronized (mProxyLock) {
126 if (!file.equals(mCurrentPac)) {
127 setCurrentProxyScript(file);
128 }
129 }
Jason Monkd4434792013-09-23 13:32:39 -0400130 mHasDownloaded = true;
131 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400132 longSchedule();
133 } else {
134 reschedule();
135 }
136 }
137 };
138
Paul Jensen7d2198b2016-08-22 09:15:40 -0400139 private final Handler mNetThreadHandler;
140
Jason Monk602b2322013-07-03 17:04:33 -0400141 class PacRefreshIntentReceiver extends BroadcastReceiver {
142 public void onReceive(Context context, Intent intent) {
Paul Jensen7d2198b2016-08-22 09:15:40 -0400143 mNetThreadHandler.post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400144 }
145 }
146
Jason Monkdecd2952013-10-10 14:02:51 -0400147 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400148 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400149 mLastPort = -1;
Chalard Jean3c443fc2018-06-07 18:37:59 +0900150 final HandlerThread netThread = new HandlerThread("android.pacmanager",
151 android.os.Process.THREAD_PRIORITY_DEFAULT);
152 netThread.start();
153 mNetThreadHandler = new Handler(netThread.getLooper());
Jason Monk602b2322013-07-03 17:04:33 -0400154
155 mPacRefreshIntent = PendingIntent.getBroadcast(
156 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
157 context.registerReceiver(new PacRefreshIntentReceiver(),
158 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400159 mConnectivityHandler = handler;
160 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400161 }
162
163 private AlarmManager getAlarmManager() {
164 if (mAlarmManager == null) {
165 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
166 }
167 return mAlarmManager;
168 }
169
Jason Monk6f8a68f2013-08-23 19:21:25 -0400170 /**
171 * Updates the PAC Manager with current Proxy information. This is called by
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900172 * the ProxyTracker directly before a broadcast takes place to allow
Jason Monk6f8a68f2013-08-23 19:21:25 -0400173 * the PacManager to indicate that the broadcast should not be sent and the
174 * PacManager will trigger a new broadcast when it is ready.
175 *
176 * @param proxy Proxy information that is about to be broadcast.
Chalard Jean69452312018-06-11 16:58:52 +0900177 * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
Jason Monk6f8a68f2013-08-23 19:21:25 -0400178 */
Chalard Jean69452312018-06-11 16:58:52 +0900179 synchronized ToSendOrNotToSendBroadcast setCurrentProxyScriptUrl(ProxyInfo proxy) {
Jason Monk83520b92014-05-09 15:16:06 -0400180 if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400181 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400182 // Allow to send broadcast, nothing to do.
Chalard Jean69452312018-06-11 16:58:52 +0900183 return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
Jason Monkdecd2952013-10-10 14:02:51 -0400184 }
Daichi Hirono4010fe42017-03-17 09:12:12 +0900185 mPacUrl = proxy.getPacFileUrl();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400186 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400187 mHasSentBroadcast = false;
188 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400189 getAlarmManager().cancel(mPacRefreshIntent);
190 bind();
Chalard Jean69452312018-06-11 16:58:52 +0900191 return ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400192 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400193 getAlarmManager().cancel(mPacRefreshIntent);
194 synchronized (mProxyLock) {
Jason Monkf6b46cb2014-09-19 12:46:19 -0400195 mPacUrl = Uri.EMPTY;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400196 mCurrentPac = null;
197 if (mProxyService != null) {
198 try {
199 mProxyService.stopPacSystem();
200 } catch (RemoteException e) {
201 Log.w(TAG, "Failed to stop PAC service", e);
202 } finally {
203 unbind();
204 }
205 }
Jason Monk602b2322013-07-03 17:04:33 -0400206 }
Chalard Jean69452312018-06-11 16:58:52 +0900207 return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400208 }
209 }
210
211 /**
212 * Does a post and reports back the status code.
213 *
Chalard Jean3c443fc2018-06-07 18:37:59 +0900214 * @throws IOException if the URL is malformed, or the PAC file is too big.
Jason Monk602b2322013-07-03 17:04:33 -0400215 */
Jason Monk83520b92014-05-09 15:16:06 -0400216 private static String get(Uri pacUri) throws IOException {
217 URL url = new URL(pacUri.toString());
Jason Monk602b2322013-07-03 17:04:33 -0400218 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Paul Jensen7d2198b2016-08-22 09:15:40 -0400219 long contentLength = -1;
220 try {
221 contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
222 } catch (NumberFormatException e) {
223 // Ignore
224 }
225 if (contentLength > MAX_PAC_SIZE) {
226 throw new IOException("PAC too big: " + contentLength + " bytes");
227 }
228 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
229 byte[] buffer = new byte[1024];
230 int count;
231 while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
232 bytes.write(buffer, 0, count);
233 if (bytes.size() > MAX_PAC_SIZE) {
234 throw new IOException("PAC too big");
235 }
236 }
237 return bytes.toString();
Jason Monk602b2322013-07-03 17:04:33 -0400238 }
239
240 private int getNextDelay(int currentDelay) {
241 if (++currentDelay > DELAY_4) {
242 return DELAY_4;
243 }
244 return currentDelay;
245 }
246
247 private void longSchedule() {
248 mCurrentDelay = DELAY_1;
249 setDownloadIn(DELAY_LONG);
250 }
251
252 private void reschedule() {
253 mCurrentDelay = getNextDelay(mCurrentDelay);
254 setDownloadIn(mCurrentDelay);
255 }
256
257 private String getPacChangeDelay() {
258 final ContentResolver cr = mContext.getContentResolver();
259
Chalard Jean3c443fc2018-06-07 18:37:59 +0900260 // Check system properties for the default value then use secure settings value, if any.
Jason Monk602b2322013-07-03 17:04:33 -0400261 String defaultDelay = SystemProperties.get(
262 "conn." + Settings.Global.PAC_CHANGE_DELAY,
263 DEFAULT_DELAYS);
264 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
265 return (val == null) ? defaultDelay : val;
266 }
267
268 private long getDownloadDelay(int delayIndex) {
269 String[] list = getPacChangeDelay().split(" ");
270 if (delayIndex < list.length) {
271 return Long.parseLong(list[delayIndex]);
272 }
273 return 0;
274 }
275
276 private void setDownloadIn(int delayIndex) {
277 long delay = getDownloadDelay(delayIndex);
278 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
279 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
280 }
281
Chalard Jean3c443fc2018-06-07 18:37:59 +0900282 private void setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700283 if (mProxyService == null) {
284 Log.e(TAG, "setCurrentProxyScript: no proxy service");
Chiachang Wangc517a6f2019-01-17 10:18:39 +0800285 return;
Wink Savillea48ad8b2013-08-10 11:22:31 -0700286 }
Jason Monk602b2322013-07-03 17:04:33 -0400287 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400288 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400289 mCurrentPac = script;
290 } catch (RemoteException e) {
291 Log.e(TAG, "Unable to set PAC file", e);
292 }
Jason Monk602b2322013-07-03 17:04:33 -0400293 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400294
295 private void bind() {
296 if (mContext == null) {
297 Log.e(TAG, "No context for binding");
298 return;
299 }
300 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400301 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monkbc018d82013-09-17 16:37:38 -0400302 if ((mProxyConnection != null) && (mConnection != null)) {
Chalard Jean69452312018-06-11 16:58:52 +0900303 // Already bound: no need to bind again, just download the new file.
Paul Jensenbaae57a2016-09-19 08:53:20 -0400304 mNetThreadHandler.post(mPacDownloader);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400305 return;
306 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400307 mConnection = new ServiceConnection() {
308 @Override
309 public void onServiceDisconnected(ComponentName component) {
310 synchronized (mProxyLock) {
311 mProxyService = null;
312 }
313 }
314
315 @Override
316 public void onServiceConnected(ComponentName component, IBinder binder) {
317 synchronized (mProxyLock) {
318 try {
Jason Monkda205a72013-08-21 15:57:30 -0400319 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400320 + binder.getInterfaceDescriptor());
321 } catch (RemoteException e1) {
322 Log.e(TAG, "Remote Exception", e1);
323 }
Jason Monkda205a72013-08-21 15:57:30 -0400324 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400325 mProxyService = IProxyService.Stub.asInterface(binder);
326 if (mProxyService == null) {
327 Log.e(TAG, "No proxy service");
328 } else {
329 try {
330 mProxyService.startPacSystem();
331 } catch (RemoteException e) {
332 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
333 }
Paul Jensen7d2198b2016-08-22 09:15:40 -0400334 mNetThreadHandler.post(mPacDownloader);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400335 }
336 }
337 }
338 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400339 mContext.bindService(intent, mConnection,
340 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400341
342 intent = new Intent();
343 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
344 mProxyConnection = new ServiceConnection() {
345 @Override
346 public void onServiceDisconnected(ComponentName component) {
347 }
348
349 @Override
350 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400351 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
352 if (callbackService != null) {
353 try {
354 callbackService.getProxyPort(new IProxyPortListener.Stub() {
355 @Override
Chalard Jean3c443fc2018-06-07 18:37:59 +0900356 public void setProxyPort(int port) {
Jason Monkd4434792013-09-23 13:32:39 -0400357 if (mLastPort != -1) {
358 // Always need to send if port changed
359 mHasSentBroadcast = false;
360 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400361 mLastPort = port;
362 if (port != -1) {
363 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400364 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400365 } else {
366 Log.e(TAG, "Received invalid port from Local Proxy,"
367 + " PAC will not be operational");
368 }
369 }
370 });
371 } catch (RemoteException e) {
372 e.printStackTrace();
373 }
374 }
Jason Monkda205a72013-08-21 15:57:30 -0400375 }
376 };
377 mContext.bindService(intent, mProxyConnection,
378 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400379 }
380
381 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400382 if (mConnection != null) {
383 mContext.unbindService(mConnection);
384 mConnection = null;
385 }
386 if (mProxyConnection != null) {
387 mContext.unbindService(mProxyConnection);
388 mProxyConnection = null;
389 }
390 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400391 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400392 }
393
Jason Monk207900c2014-04-25 15:00:09 -0400394 private void sendPacBroadcast(ProxyInfo proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400395 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400396 }
Jason Monkd4434792013-09-23 13:32:39 -0400397
398 private synchronized void sendProxyIfNeeded() {
399 if (!mHasDownloaded || (mLastPort == -1)) {
400 return;
401 }
402 if (!mHasSentBroadcast) {
Jason Monk207900c2014-04-25 15:00:09 -0400403 sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
Jason Monkd4434792013-09-23 13:32:39 -0400404 mHasSentBroadcast = true;
405 }
406 }
Jason Monk602b2322013-07-03 17:04:33 -0400407}