blob: 3a27fcb352aa01697ac908554f4283b38ea8617a [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
Jason Monk9ced3cd2013-08-12 16:42:38 -040046import libcore.io.Streams;
Jason Monk602b2322013-07-03 17:04:33 -040047
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
Jason Monk602b2322013-07-03 17:04:33 -040074 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040075 @GuardedBy("mProxyLock")
Daichi Hirono4010fe42017-03-17 09:12:12 +090076 private volatile Uri mPacUrl = Uri.EMPTY;
Jason Monk602b2322013-07-03 17:04:33 -040077
78 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040079 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040080 private IProxyService mProxyService;
81 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040082 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040083 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040084 private Context mContext;
85
86 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040087 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040088
Daichi Hirono4010fe42017-03-17 09:12:12 +090089 private volatile boolean mHasSentBroadcast;
90 private volatile boolean mHasDownloaded;
Jason Monkd4434792013-09-23 13:32:39 -040091
Jason Monkdecd2952013-10-10 14:02:51 -040092 private Handler mConnectivityHandler;
93 private int mProxyMessage;
94
Jason Monk9ced3cd2013-08-12 16:42:38 -040095 /**
Daichi Hirono4010fe42017-03-17 09:12:12 +090096 * Used for locking when setting mProxyService and all references to mCurrentPac.
Jason Monk9ced3cd2013-08-12 16:42:38 -040097 */
98 private final Object mProxyLock = new Object();
99
Daichi Hirono4010fe42017-03-17 09:12:12 +0900100 /**
101 * Runnable to download PAC script.
102 * The behavior relies on the assamption it always run on mNetThread to guarantee that the
103 * latest data fetched from mPacUrl is stored in mProxyService.
104 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400105 private Runnable mPacDownloader = new Runnable() {
106 @Override
Daichi Hirono4010fe42017-03-17 09:12:12 +0900107 @WorkerThread
Jason Monk9ced3cd2013-08-12 16:42:38 -0400108 public void run() {
109 String file;
Daichi Hirono4010fe42017-03-17 09:12:12 +0900110 final Uri pacUrl = mPacUrl;
111 if (Uri.EMPTY.equals(pacUrl)) return;
112 final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
113 try {
114 file = get(pacUrl);
115 } catch (IOException ioe) {
116 file = null;
117 Log.w(TAG, "Failed to load PAC file: " + ioe);
118 } finally {
119 TrafficStats.setThreadStatsTag(oldTag);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400120 }
121 if (file != null) {
122 synchronized (mProxyLock) {
123 if (!file.equals(mCurrentPac)) {
124 setCurrentProxyScript(file);
125 }
126 }
Jason Monkd4434792013-09-23 13:32:39 -0400127 mHasDownloaded = true;
128 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400129 longSchedule();
130 } else {
131 reschedule();
132 }
133 }
134 };
135
Paul Jensen7d2198b2016-08-22 09:15:40 -0400136 private final HandlerThread mNetThread = new HandlerThread("android.pacmanager",
137 android.os.Process.THREAD_PRIORITY_DEFAULT);
138 private final Handler mNetThreadHandler;
139
Jason Monk602b2322013-07-03 17:04:33 -0400140 class PacRefreshIntentReceiver extends BroadcastReceiver {
141 public void onReceive(Context context, Intent intent) {
Paul Jensen7d2198b2016-08-22 09:15:40 -0400142 mNetThreadHandler.post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400143 }
144 }
145
Jason Monkdecd2952013-10-10 14:02:51 -0400146 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400147 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400148 mLastPort = -1;
Paul Jensen7d2198b2016-08-22 09:15:40 -0400149 mNetThread.start();
150 mNetThreadHandler = new Handler(mNetThread.getLooper());
Jason Monk602b2322013-07-03 17:04:33 -0400151
152 mPacRefreshIntent = PendingIntent.getBroadcast(
153 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
154 context.registerReceiver(new PacRefreshIntentReceiver(),
155 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400156 mConnectivityHandler = handler;
157 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400158 }
159
160 private AlarmManager getAlarmManager() {
161 if (mAlarmManager == null) {
162 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
163 }
164 return mAlarmManager;
165 }
166
Jason Monk6f8a68f2013-08-23 19:21:25 -0400167 /**
168 * Updates the PAC Manager with current Proxy information. This is called by
169 * the ConnectivityService directly before a broadcast takes place to allow
170 * the PacManager to indicate that the broadcast should not be sent and the
171 * PacManager will trigger a new broadcast when it is ready.
172 *
173 * @param proxy Proxy information that is about to be broadcast.
174 * @return Returns true when the broadcast should not be sent
175 */
Jason Monk207900c2014-04-25 15:00:09 -0400176 public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
Jason Monk83520b92014-05-09 15:16:06 -0400177 if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400178 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400179 // Allow to send broadcast, nothing to do.
180 return false;
181 }
Daichi Hirono4010fe42017-03-17 09:12:12 +0900182 mPacUrl = proxy.getPacFileUrl();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400183 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400184 mHasSentBroadcast = false;
185 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400186 getAlarmManager().cancel(mPacRefreshIntent);
187 bind();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400188 return true;
Jason Monk602b2322013-07-03 17:04:33 -0400189 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400190 getAlarmManager().cancel(mPacRefreshIntent);
191 synchronized (mProxyLock) {
Jason Monkf6b46cb2014-09-19 12:46:19 -0400192 mPacUrl = Uri.EMPTY;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400193 mCurrentPac = null;
194 if (mProxyService != null) {
195 try {
196 mProxyService.stopPacSystem();
197 } catch (RemoteException e) {
198 Log.w(TAG, "Failed to stop PAC service", e);
199 } finally {
200 unbind();
201 }
202 }
Jason Monk602b2322013-07-03 17:04:33 -0400203 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400204 return false;
Jason Monk602b2322013-07-03 17:04:33 -0400205 }
206 }
207
208 /**
209 * Does a post and reports back the status code.
210 *
211 * @throws IOException
212 */
Jason Monk83520b92014-05-09 15:16:06 -0400213 private static String get(Uri pacUri) throws IOException {
214 URL url = new URL(pacUri.toString());
Jason Monk602b2322013-07-03 17:04:33 -0400215 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Paul Jensen7d2198b2016-08-22 09:15:40 -0400216 long contentLength = -1;
217 try {
218 contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
219 } catch (NumberFormatException e) {
220 // Ignore
221 }
222 if (contentLength > MAX_PAC_SIZE) {
223 throw new IOException("PAC too big: " + contentLength + " bytes");
224 }
225 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
226 byte[] buffer = new byte[1024];
227 int count;
228 while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
229 bytes.write(buffer, 0, count);
230 if (bytes.size() > MAX_PAC_SIZE) {
231 throw new IOException("PAC too big");
232 }
233 }
234 return bytes.toString();
Jason Monk602b2322013-07-03 17:04:33 -0400235 }
236
237 private int getNextDelay(int currentDelay) {
238 if (++currentDelay > DELAY_4) {
239 return DELAY_4;
240 }
241 return currentDelay;
242 }
243
244 private void longSchedule() {
245 mCurrentDelay = DELAY_1;
246 setDownloadIn(DELAY_LONG);
247 }
248
249 private void reschedule() {
250 mCurrentDelay = getNextDelay(mCurrentDelay);
251 setDownloadIn(mCurrentDelay);
252 }
253
254 private String getPacChangeDelay() {
255 final ContentResolver cr = mContext.getContentResolver();
256
257 /** Check system properties for the default value then use secure settings value, if any. */
258 String defaultDelay = SystemProperties.get(
259 "conn." + Settings.Global.PAC_CHANGE_DELAY,
260 DEFAULT_DELAYS);
261 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
262 return (val == null) ? defaultDelay : val;
263 }
264
265 private long getDownloadDelay(int delayIndex) {
266 String[] list = getPacChangeDelay().split(" ");
267 if (delayIndex < list.length) {
268 return Long.parseLong(list[delayIndex]);
269 }
270 return 0;
271 }
272
273 private void setDownloadIn(int delayIndex) {
274 long delay = getDownloadDelay(delayIndex);
275 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
276 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
277 }
278
279 private boolean setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700280 if (mProxyService == null) {
281 Log.e(TAG, "setCurrentProxyScript: no proxy service");
282 return false;
283 }
Jason Monk602b2322013-07-03 17:04:33 -0400284 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400285 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400286 mCurrentPac = script;
287 } catch (RemoteException e) {
288 Log.e(TAG, "Unable to set PAC file", e);
289 }
290 return true;
291 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400292
293 private void bind() {
294 if (mContext == null) {
295 Log.e(TAG, "No context for binding");
296 return;
297 }
298 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400299 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monkbc018d82013-09-17 16:37:38 -0400300 if ((mProxyConnection != null) && (mConnection != null)) {
Jason Monk4bf1d212014-09-25 13:16:32 -0400301 // Already bound no need to bind again, just download the new file.
Paul Jensenbaae57a2016-09-19 08:53:20 -0400302 mNetThreadHandler.post(mPacDownloader);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400303 return;
304 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400305 mConnection = new ServiceConnection() {
306 @Override
307 public void onServiceDisconnected(ComponentName component) {
308 synchronized (mProxyLock) {
309 mProxyService = null;
310 }
311 }
312
313 @Override
314 public void onServiceConnected(ComponentName component, IBinder binder) {
315 synchronized (mProxyLock) {
316 try {
Jason Monkda205a72013-08-21 15:57:30 -0400317 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400318 + binder.getInterfaceDescriptor());
319 } catch (RemoteException e1) {
320 Log.e(TAG, "Remote Exception", e1);
321 }
Jason Monkda205a72013-08-21 15:57:30 -0400322 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400323 mProxyService = IProxyService.Stub.asInterface(binder);
324 if (mProxyService == null) {
325 Log.e(TAG, "No proxy service");
326 } else {
327 try {
328 mProxyService.startPacSystem();
329 } catch (RemoteException e) {
330 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
331 }
Paul Jensen7d2198b2016-08-22 09:15:40 -0400332 mNetThreadHandler.post(mPacDownloader);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400333 }
334 }
335 }
336 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400337 mContext.bindService(intent, mConnection,
338 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400339
340 intent = new Intent();
341 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
342 mProxyConnection = new ServiceConnection() {
343 @Override
344 public void onServiceDisconnected(ComponentName component) {
345 }
346
347 @Override
348 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400349 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
350 if (callbackService != null) {
351 try {
352 callbackService.getProxyPort(new IProxyPortListener.Stub() {
353 @Override
354 public void setProxyPort(int port) throws RemoteException {
Jason Monkd4434792013-09-23 13:32:39 -0400355 if (mLastPort != -1) {
356 // Always need to send if port changed
357 mHasSentBroadcast = false;
358 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400359 mLastPort = port;
360 if (port != -1) {
361 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400362 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400363 } else {
364 Log.e(TAG, "Received invalid port from Local Proxy,"
365 + " PAC will not be operational");
366 }
367 }
368 });
369 } catch (RemoteException e) {
370 e.printStackTrace();
371 }
372 }
Jason Monkda205a72013-08-21 15:57:30 -0400373 }
374 };
375 mContext.bindService(intent, mProxyConnection,
376 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400377 }
378
379 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400380 if (mConnection != null) {
381 mContext.unbindService(mConnection);
382 mConnection = null;
383 }
384 if (mProxyConnection != null) {
385 mContext.unbindService(mProxyConnection);
386 mProxyConnection = null;
387 }
388 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400389 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400390 }
391
Jason Monk207900c2014-04-25 15:00:09 -0400392 private void sendPacBroadcast(ProxyInfo proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400393 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400394 }
Jason Monkd4434792013-09-23 13:32:39 -0400395
396 private synchronized void sendProxyIfNeeded() {
397 if (!mHasDownloaded || (mLastPort == -1)) {
398 return;
399 }
400 if (!mHasSentBroadcast) {
Jason Monk207900c2014-04-25 15:00:09 -0400401 sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
Jason Monkd4434792013-09-23 13:32:39 -0400402 mHasSentBroadcast = true;
403 }
404 }
Jason Monk602b2322013-07-03 17:04:33 -0400405}