blob: f6ce2dc68b999f6853736c509adccad5434bd597 [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;
Chalard Jean6a76b7e2019-04-09 11:16:56 +090042import com.android.internal.util.TrafficStatsConstants;
Jason Monk6f8a68f2013-08-23 19:21:25 -040043import com.android.net.IProxyCallback;
44import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040045import com.android.net.IProxyService;
46
Paul Jensen7d2198b2016-08-22 09:15:40 -040047import java.io.ByteArrayOutputStream;
Jason Monk602b2322013-07-03 17:04:33 -040048import java.io.IOException;
Jason Monk602b2322013-07-03 17:04:33 -040049import java.net.URL;
50import java.net.URLConnection;
51
52/**
53 * @hide
54 */
Jason Monk9ced3cd2013-08-12 16:42:38 -040055public class PacManager {
Chalard Jean918a68b2018-01-19 17:00:47 +090056 private static final String PAC_PACKAGE = "com.android.pacprocessor";
57 private static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
58 private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040059
Chalard Jean918a68b2018-01-19 17:00:47 +090060 private static final String PROXY_PACKAGE = "com.android.proxyhandler";
61 private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040062
Jason Monk9ced3cd2013-08-12 16:42:38 -040063 private static final String TAG = "PacManager";
Jason Monk602b2322013-07-03 17:04:33 -040064
65 private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
66
67 private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
68 private static final int DELAY_1 = 0;
69 private static final int DELAY_4 = 3;
70 private static final int DELAY_LONG = 4;
Paul Jensen7d2198b2016-08-22 09:15:40 -040071 private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
Jason Monk602b2322013-07-03 17:04:33 -040072
Chalard Jean69452312018-06-11 16:58:52 +090073 // Return values for #setCurrentProxyScriptUrl
Chalard Jean29f63af2018-10-15 14:27:15 +090074 public static final boolean DONT_SEND_BROADCAST = false;
75 public static final boolean DO_SEND_BROADCAST = true;
Chalard Jean69452312018-06-11 16:58:52 +090076
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;
Chalard Jean6a76b7e2019-04-09 11:16:56 +0900115 final int oldTag = TrafficStats.getAndSetThreadStatsTag(
116 TrafficStatsConstants.TAG_SYSTEM_PAC);
Daichi Hirono4010fe42017-03-17 09:12:12 +0900117 try {
118 file = get(pacUrl);
119 } catch (IOException ioe) {
120 file = null;
121 Log.w(TAG, "Failed to load PAC file: " + ioe);
122 } finally {
123 TrafficStats.setThreadStatsTag(oldTag);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400124 }
125 if (file != null) {
126 synchronized (mProxyLock) {
127 if (!file.equals(mCurrentPac)) {
128 setCurrentProxyScript(file);
129 }
130 }
Jason Monkd4434792013-09-23 13:32:39 -0400131 mHasDownloaded = true;
132 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400133 longSchedule();
134 } else {
135 reschedule();
136 }
137 }
138 };
139
Paul Jensen7d2198b2016-08-22 09:15:40 -0400140 private final Handler mNetThreadHandler;
141
Jason Monk602b2322013-07-03 17:04:33 -0400142 class PacRefreshIntentReceiver extends BroadcastReceiver {
143 public void onReceive(Context context, Intent intent) {
Paul Jensen7d2198b2016-08-22 09:15:40 -0400144 mNetThreadHandler.post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400145 }
146 }
147
Jason Monkdecd2952013-10-10 14:02:51 -0400148 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400149 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400150 mLastPort = -1;
Chalard Jean3c443fc2018-06-07 18:37:59 +0900151 final HandlerThread netThread = new HandlerThread("android.pacmanager",
152 android.os.Process.THREAD_PRIORITY_DEFAULT);
153 netThread.start();
154 mNetThreadHandler = new Handler(netThread.getLooper());
Jason Monk602b2322013-07-03 17:04:33 -0400155
156 mPacRefreshIntent = PendingIntent.getBroadcast(
157 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
158 context.registerReceiver(new PacRefreshIntentReceiver(),
159 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400160 mConnectivityHandler = handler;
161 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400162 }
163
164 private AlarmManager getAlarmManager() {
165 if (mAlarmManager == null) {
166 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
167 }
168 return mAlarmManager;
169 }
170
Jason Monk6f8a68f2013-08-23 19:21:25 -0400171 /**
172 * Updates the PAC Manager with current Proxy information. This is called by
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900173 * the ProxyTracker directly before a broadcast takes place to allow
Jason Monk6f8a68f2013-08-23 19:21:25 -0400174 * the PacManager to indicate that the broadcast should not be sent and the
175 * PacManager will trigger a new broadcast when it is ready.
176 *
177 * @param proxy Proxy information that is about to be broadcast.
Chalard Jean69452312018-06-11 16:58:52 +0900178 * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
Jason Monk6f8a68f2013-08-23 19:21:25 -0400179 */
Chalard Jean29f63af2018-10-15 14:27:15 +0900180 synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
Jason Monk83520b92014-05-09 15:16:06 -0400181 if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400182 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400183 // Allow to send broadcast, nothing to do.
Chalard Jean29f63af2018-10-15 14:27:15 +0900184 return DO_SEND_BROADCAST;
Jason Monkdecd2952013-10-10 14:02:51 -0400185 }
Daichi Hirono4010fe42017-03-17 09:12:12 +0900186 mPacUrl = proxy.getPacFileUrl();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400187 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400188 mHasSentBroadcast = false;
189 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400190 getAlarmManager().cancel(mPacRefreshIntent);
191 bind();
Chalard Jean29f63af2018-10-15 14:27:15 +0900192 return DONT_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400193 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400194 getAlarmManager().cancel(mPacRefreshIntent);
195 synchronized (mProxyLock) {
Jason Monkf6b46cb2014-09-19 12:46:19 -0400196 mPacUrl = Uri.EMPTY;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400197 mCurrentPac = null;
198 if (mProxyService != null) {
199 try {
200 mProxyService.stopPacSystem();
201 } catch (RemoteException e) {
202 Log.w(TAG, "Failed to stop PAC service", e);
203 } finally {
204 unbind();
205 }
206 }
Jason Monk602b2322013-07-03 17:04:33 -0400207 }
Chalard Jean29f63af2018-10-15 14:27:15 +0900208 return DO_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400209 }
210 }
211
212 /**
213 * Does a post and reports back the status code.
214 *
Chalard Jean3c443fc2018-06-07 18:37:59 +0900215 * @throws IOException if the URL is malformed, or the PAC file is too big.
Jason Monk602b2322013-07-03 17:04:33 -0400216 */
Jason Monk83520b92014-05-09 15:16:06 -0400217 private static String get(Uri pacUri) throws IOException {
218 URL url = new URL(pacUri.toString());
Jason Monk602b2322013-07-03 17:04:33 -0400219 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Paul Jensen7d2198b2016-08-22 09:15:40 -0400220 long contentLength = -1;
221 try {
222 contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
223 } catch (NumberFormatException e) {
224 // Ignore
225 }
226 if (contentLength > MAX_PAC_SIZE) {
227 throw new IOException("PAC too big: " + contentLength + " bytes");
228 }
229 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
230 byte[] buffer = new byte[1024];
231 int count;
232 while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
233 bytes.write(buffer, 0, count);
234 if (bytes.size() > MAX_PAC_SIZE) {
235 throw new IOException("PAC too big");
236 }
237 }
238 return bytes.toString();
Jason Monk602b2322013-07-03 17:04:33 -0400239 }
240
241 private int getNextDelay(int currentDelay) {
242 if (++currentDelay > DELAY_4) {
243 return DELAY_4;
244 }
245 return currentDelay;
246 }
247
248 private void longSchedule() {
249 mCurrentDelay = DELAY_1;
250 setDownloadIn(DELAY_LONG);
251 }
252
253 private void reschedule() {
254 mCurrentDelay = getNextDelay(mCurrentDelay);
255 setDownloadIn(mCurrentDelay);
256 }
257
258 private String getPacChangeDelay() {
259 final ContentResolver cr = mContext.getContentResolver();
260
Chalard Jean3c443fc2018-06-07 18:37:59 +0900261 // Check system properties for the default value then use secure settings value, if any.
Jason Monk602b2322013-07-03 17:04:33 -0400262 String defaultDelay = SystemProperties.get(
263 "conn." + Settings.Global.PAC_CHANGE_DELAY,
264 DEFAULT_DELAYS);
265 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
266 return (val == null) ? defaultDelay : val;
267 }
268
269 private long getDownloadDelay(int delayIndex) {
270 String[] list = getPacChangeDelay().split(" ");
271 if (delayIndex < list.length) {
272 return Long.parseLong(list[delayIndex]);
273 }
274 return 0;
275 }
276
277 private void setDownloadIn(int delayIndex) {
278 long delay = getDownloadDelay(delayIndex);
279 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
280 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
281 }
282
Chalard Jean3c443fc2018-06-07 18:37:59 +0900283 private void setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700284 if (mProxyService == null) {
285 Log.e(TAG, "setCurrentProxyScript: no proxy service");
Chiachang Wangc517a6f2019-01-17 10:18:39 +0800286 return;
Wink Savillea48ad8b2013-08-10 11:22:31 -0700287 }
Jason Monk602b2322013-07-03 17:04:33 -0400288 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400289 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400290 mCurrentPac = script;
291 } catch (RemoteException e) {
292 Log.e(TAG, "Unable to set PAC file", e);
293 }
Jason Monk602b2322013-07-03 17:04:33 -0400294 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400295
296 private void bind() {
297 if (mContext == null) {
298 Log.e(TAG, "No context for binding");
299 return;
300 }
301 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400302 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monkbc018d82013-09-17 16:37:38 -0400303 if ((mProxyConnection != null) && (mConnection != null)) {
Chalard Jean69452312018-06-11 16:58:52 +0900304 // Already bound: no need to bind again, just download the new file.
Paul Jensenbaae57a2016-09-19 08:53:20 -0400305 mNetThreadHandler.post(mPacDownloader);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400306 return;
307 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400308 mConnection = new ServiceConnection() {
309 @Override
310 public void onServiceDisconnected(ComponentName component) {
311 synchronized (mProxyLock) {
312 mProxyService = null;
313 }
314 }
315
316 @Override
317 public void onServiceConnected(ComponentName component, IBinder binder) {
318 synchronized (mProxyLock) {
319 try {
Jason Monkda205a72013-08-21 15:57:30 -0400320 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400321 + binder.getInterfaceDescriptor());
322 } catch (RemoteException e1) {
323 Log.e(TAG, "Remote Exception", e1);
324 }
Jason Monkda205a72013-08-21 15:57:30 -0400325 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400326 mProxyService = IProxyService.Stub.asInterface(binder);
327 if (mProxyService == null) {
328 Log.e(TAG, "No proxy service");
329 } else {
330 try {
331 mProxyService.startPacSystem();
332 } catch (RemoteException e) {
333 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
334 }
Paul Jensen7d2198b2016-08-22 09:15:40 -0400335 mNetThreadHandler.post(mPacDownloader);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400336 }
337 }
338 }
339 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400340 mContext.bindService(intent, mConnection,
341 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400342
343 intent = new Intent();
344 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
345 mProxyConnection = new ServiceConnection() {
346 @Override
347 public void onServiceDisconnected(ComponentName component) {
348 }
349
350 @Override
351 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400352 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
353 if (callbackService != null) {
354 try {
355 callbackService.getProxyPort(new IProxyPortListener.Stub() {
356 @Override
Chalard Jean3c443fc2018-06-07 18:37:59 +0900357 public void setProxyPort(int port) {
Jason Monkd4434792013-09-23 13:32:39 -0400358 if (mLastPort != -1) {
359 // Always need to send if port changed
360 mHasSentBroadcast = false;
361 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400362 mLastPort = port;
363 if (port != -1) {
364 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400365 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400366 } else {
367 Log.e(TAG, "Received invalid port from Local Proxy,"
368 + " PAC will not be operational");
369 }
370 }
371 });
372 } catch (RemoteException e) {
373 e.printStackTrace();
374 }
375 }
Jason Monkda205a72013-08-21 15:57:30 -0400376 }
377 };
378 mContext.bindService(intent, mProxyConnection,
379 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400380 }
381
382 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400383 if (mConnection != null) {
384 mContext.unbindService(mConnection);
385 mConnection = null;
386 }
387 if (mProxyConnection != null) {
388 mContext.unbindService(mProxyConnection);
389 mProxyConnection = null;
390 }
391 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400392 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400393 }
394
Jason Monk207900c2014-04-25 15:00:09 -0400395 private void sendPacBroadcast(ProxyInfo proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400396 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400397 }
Jason Monkd4434792013-09-23 13:32:39 -0400398
399 private synchronized void sendProxyIfNeeded() {
400 if (!mHasDownloaded || (mLastPort == -1)) {
401 return;
402 }
403 if (!mHasSentBroadcast) {
Jason Monk207900c2014-04-25 15:00:09 -0400404 sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
Jason Monkd4434792013-09-23 13:32:39 -0400405 mHasSentBroadcast = true;
406 }
407 }
Jason Monk602b2322013-07-03 17:04:33 -0400408}