blob: 1ac09ad462a11b3411e1836154d2538a1f1e1754 [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
Chalard Jean29f63af2018-10-15 14:27:15 +090073 public static final boolean DONT_SEND_BROADCAST = false;
74 public static final boolean DO_SEND_BROADCAST = true;
Chalard Jean69452312018-06-11 16:58:52 +090075
Jason Monk602b2322013-07-03 17:04:33 -040076 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040077 @GuardedBy("mProxyLock")
Daichi Hirono4010fe42017-03-17 09:12:12 +090078 private volatile Uri mPacUrl = Uri.EMPTY;
Jason Monk602b2322013-07-03 17:04:33 -040079
80 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040081 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040082 private IProxyService mProxyService;
83 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040084 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040085 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040086 private Context mContext;
87
88 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040089 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040090
Daichi Hirono4010fe42017-03-17 09:12:12 +090091 private volatile boolean mHasSentBroadcast;
92 private volatile boolean mHasDownloaded;
Jason Monkd4434792013-09-23 13:32:39 -040093
Jason Monkdecd2952013-10-10 14:02:51 -040094 private Handler mConnectivityHandler;
Chalard Jean3c443fc2018-06-07 18:37:59 +090095 private final int mProxyMessage;
Jason Monkdecd2952013-10-10 14:02:51 -040096
Jason Monk9ced3cd2013-08-12 16:42:38 -040097 /**
Daichi Hirono4010fe42017-03-17 09:12:12 +090098 * Used for locking when setting mProxyService and all references to mCurrentPac.
Jason Monk9ced3cd2013-08-12 16:42:38 -040099 */
100 private final Object mProxyLock = new Object();
101
Daichi Hirono4010fe42017-03-17 09:12:12 +0900102 /**
103 * Runnable to download PAC script.
Chalard Jean3c443fc2018-06-07 18:37:59 +0900104 * The behavior relies on the assumption it always runs on mNetThread to guarantee that the
Daichi Hirono4010fe42017-03-17 09:12:12 +0900105 * latest data fetched from mPacUrl is stored in mProxyService.
106 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400107 private Runnable mPacDownloader = new Runnable() {
108 @Override
Daichi Hirono4010fe42017-03-17 09:12:12 +0900109 @WorkerThread
Jason Monk9ced3cd2013-08-12 16:42:38 -0400110 public void run() {
111 String file;
Daichi Hirono4010fe42017-03-17 09:12:12 +0900112 final Uri pacUrl = mPacUrl;
113 if (Uri.EMPTY.equals(pacUrl)) return;
114 final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
115 try {
116 file = get(pacUrl);
117 } catch (IOException ioe) {
118 file = null;
119 Log.w(TAG, "Failed to load PAC file: " + ioe);
120 } finally {
121 TrafficStats.setThreadStatsTag(oldTag);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400122 }
123 if (file != null) {
124 synchronized (mProxyLock) {
125 if (!file.equals(mCurrentPac)) {
126 setCurrentProxyScript(file);
127 }
128 }
Jason Monkd4434792013-09-23 13:32:39 -0400129 mHasDownloaded = true;
130 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400131 longSchedule();
132 } else {
133 reschedule();
134 }
135 }
136 };
137
Paul Jensen7d2198b2016-08-22 09:15:40 -0400138 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;
Chalard Jean3c443fc2018-06-07 18:37:59 +0900149 final HandlerThread netThread = new HandlerThread("android.pacmanager",
150 android.os.Process.THREAD_PRIORITY_DEFAULT);
151 netThread.start();
152 mNetThreadHandler = new Handler(netThread.getLooper());
Jason Monk602b2322013-07-03 17:04:33 -0400153
154 mPacRefreshIntent = PendingIntent.getBroadcast(
155 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
156 context.registerReceiver(new PacRefreshIntentReceiver(),
157 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400158 mConnectivityHandler = handler;
159 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400160 }
161
162 private AlarmManager getAlarmManager() {
163 if (mAlarmManager == null) {
164 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
165 }
166 return mAlarmManager;
167 }
168
Jason Monk6f8a68f2013-08-23 19:21:25 -0400169 /**
170 * Updates the PAC Manager with current Proxy information. This is called by
Chalard Jeand82ba6d2018-06-08 12:41:21 +0900171 * the ProxyTracker directly before a broadcast takes place to allow
Jason Monk6f8a68f2013-08-23 19:21:25 -0400172 * the PacManager to indicate that the broadcast should not be sent and the
173 * PacManager will trigger a new broadcast when it is ready.
174 *
175 * @param proxy Proxy information that is about to be broadcast.
Chalard Jean69452312018-06-11 16:58:52 +0900176 * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
Jason Monk6f8a68f2013-08-23 19:21:25 -0400177 */
Chalard Jean29f63af2018-10-15 14:27:15 +0900178 synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
Jason Monk83520b92014-05-09 15:16:06 -0400179 if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400180 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400181 // Allow to send broadcast, nothing to do.
Chalard Jean29f63af2018-10-15 14:27:15 +0900182 return DO_SEND_BROADCAST;
Jason Monkdecd2952013-10-10 14:02:51 -0400183 }
Daichi Hirono4010fe42017-03-17 09:12:12 +0900184 mPacUrl = proxy.getPacFileUrl();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400185 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400186 mHasSentBroadcast = false;
187 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400188 getAlarmManager().cancel(mPacRefreshIntent);
189 bind();
Chalard Jean29f63af2018-10-15 14:27:15 +0900190 return DONT_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400191 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400192 getAlarmManager().cancel(mPacRefreshIntent);
193 synchronized (mProxyLock) {
Jason Monkf6b46cb2014-09-19 12:46:19 -0400194 mPacUrl = Uri.EMPTY;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400195 mCurrentPac = null;
196 if (mProxyService != null) {
197 try {
198 mProxyService.stopPacSystem();
199 } catch (RemoteException e) {
200 Log.w(TAG, "Failed to stop PAC service", e);
201 } finally {
202 unbind();
203 }
204 }
Jason Monk602b2322013-07-03 17:04:33 -0400205 }
Chalard Jean29f63af2018-10-15 14:27:15 +0900206 return DO_SEND_BROADCAST;
Jason Monk602b2322013-07-03 17:04:33 -0400207 }
208 }
209
210 /**
211 * Does a post and reports back the status code.
212 *
Chalard Jean3c443fc2018-06-07 18:37:59 +0900213 * @throws IOException if the URL is malformed, or the PAC file is too big.
Jason Monk602b2322013-07-03 17:04:33 -0400214 */
Jason Monk83520b92014-05-09 15:16:06 -0400215 private static String get(Uri pacUri) throws IOException {
216 URL url = new URL(pacUri.toString());
Jason Monk602b2322013-07-03 17:04:33 -0400217 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Paul Jensen7d2198b2016-08-22 09:15:40 -0400218 long contentLength = -1;
219 try {
220 contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
221 } catch (NumberFormatException e) {
222 // Ignore
223 }
224 if (contentLength > MAX_PAC_SIZE) {
225 throw new IOException("PAC too big: " + contentLength + " bytes");
226 }
227 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
228 byte[] buffer = new byte[1024];
229 int count;
230 while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
231 bytes.write(buffer, 0, count);
232 if (bytes.size() > MAX_PAC_SIZE) {
233 throw new IOException("PAC too big");
234 }
235 }
236 return bytes.toString();
Jason Monk602b2322013-07-03 17:04:33 -0400237 }
238
239 private int getNextDelay(int currentDelay) {
240 if (++currentDelay > DELAY_4) {
241 return DELAY_4;
242 }
243 return currentDelay;
244 }
245
246 private void longSchedule() {
247 mCurrentDelay = DELAY_1;
248 setDownloadIn(DELAY_LONG);
249 }
250
251 private void reschedule() {
252 mCurrentDelay = getNextDelay(mCurrentDelay);
253 setDownloadIn(mCurrentDelay);
254 }
255
256 private String getPacChangeDelay() {
257 final ContentResolver cr = mContext.getContentResolver();
258
Chalard Jean3c443fc2018-06-07 18:37:59 +0900259 // Check system properties for the default value then use secure settings value, if any.
Jason Monk602b2322013-07-03 17:04:33 -0400260 String defaultDelay = SystemProperties.get(
261 "conn." + Settings.Global.PAC_CHANGE_DELAY,
262 DEFAULT_DELAYS);
263 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
264 return (val == null) ? defaultDelay : val;
265 }
266
267 private long getDownloadDelay(int delayIndex) {
268 String[] list = getPacChangeDelay().split(" ");
269 if (delayIndex < list.length) {
270 return Long.parseLong(list[delayIndex]);
271 }
272 return 0;
273 }
274
275 private void setDownloadIn(int delayIndex) {
276 long delay = getDownloadDelay(delayIndex);
277 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
278 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
279 }
280
Chalard Jean3c443fc2018-06-07 18:37:59 +0900281 private void setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700282 if (mProxyService == null) {
283 Log.e(TAG, "setCurrentProxyScript: no proxy service");
Chiachang Wangc517a6f2019-01-17 10:18:39 +0800284 return;
Wink Savillea48ad8b2013-08-10 11:22:31 -0700285 }
Jason Monk602b2322013-07-03 17:04:33 -0400286 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400287 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400288 mCurrentPac = script;
289 } catch (RemoteException e) {
290 Log.e(TAG, "Unable to set PAC file", e);
291 }
Jason Monk602b2322013-07-03 17:04:33 -0400292 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400293
294 private void bind() {
295 if (mContext == null) {
296 Log.e(TAG, "No context for binding");
297 return;
298 }
299 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400300 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monkbc018d82013-09-17 16:37:38 -0400301 if ((mProxyConnection != null) && (mConnection != null)) {
Chalard Jean69452312018-06-11 16:58:52 +0900302 // Already bound: no need to bind again, just download the new file.
Paul Jensenbaae57a2016-09-19 08:53:20 -0400303 mNetThreadHandler.post(mPacDownloader);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400304 return;
305 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400306 mConnection = new ServiceConnection() {
307 @Override
308 public void onServiceDisconnected(ComponentName component) {
309 synchronized (mProxyLock) {
310 mProxyService = null;
311 }
312 }
313
314 @Override
315 public void onServiceConnected(ComponentName component, IBinder binder) {
316 synchronized (mProxyLock) {
317 try {
Jason Monkda205a72013-08-21 15:57:30 -0400318 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400319 + binder.getInterfaceDescriptor());
320 } catch (RemoteException e1) {
321 Log.e(TAG, "Remote Exception", e1);
322 }
Jason Monkda205a72013-08-21 15:57:30 -0400323 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400324 mProxyService = IProxyService.Stub.asInterface(binder);
325 if (mProxyService == null) {
326 Log.e(TAG, "No proxy service");
327 } else {
328 try {
329 mProxyService.startPacSystem();
330 } catch (RemoteException e) {
331 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
332 }
Paul Jensen7d2198b2016-08-22 09:15:40 -0400333 mNetThreadHandler.post(mPacDownloader);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400334 }
335 }
336 }
337 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400338 mContext.bindService(intent, mConnection,
339 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400340
341 intent = new Intent();
342 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
343 mProxyConnection = new ServiceConnection() {
344 @Override
345 public void onServiceDisconnected(ComponentName component) {
346 }
347
348 @Override
349 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400350 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
351 if (callbackService != null) {
352 try {
353 callbackService.getProxyPort(new IProxyPortListener.Stub() {
354 @Override
Chalard Jean3c443fc2018-06-07 18:37:59 +0900355 public void setProxyPort(int port) {
Jason Monkd4434792013-09-23 13:32:39 -0400356 if (mLastPort != -1) {
357 // Always need to send if port changed
358 mHasSentBroadcast = false;
359 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400360 mLastPort = port;
361 if (port != -1) {
362 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400363 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400364 } else {
365 Log.e(TAG, "Received invalid port from Local Proxy,"
366 + " PAC will not be operational");
367 }
368 }
369 });
370 } catch (RemoteException e) {
371 e.printStackTrace();
372 }
373 }
Jason Monkda205a72013-08-21 15:57:30 -0400374 }
375 };
376 mContext.bindService(intent, mProxyConnection,
377 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400378 }
379
380 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400381 if (mConnection != null) {
382 mContext.unbindService(mConnection);
383 mConnection = null;
384 }
385 if (mProxyConnection != null) {
386 mContext.unbindService(mProxyConnection);
387 mProxyConnection = null;
388 }
389 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400390 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400391 }
392
Jason Monk207900c2014-04-25 15:00:09 -0400393 private void sendPacBroadcast(ProxyInfo proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400394 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400395 }
Jason Monkd4434792013-09-23 13:32:39 -0400396
397 private synchronized void sendProxyIfNeeded() {
398 if (!mHasDownloaded || (mLastPort == -1)) {
399 return;
400 }
401 if (!mHasSentBroadcast) {
Jason Monk207900c2014-04-25 15:00:09 -0400402 sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
Jason Monkd4434792013-09-23 13:32:39 -0400403 mHasSentBroadcast = true;
404 }
405 }
Jason Monk602b2322013-07-03 17:04:33 -0400406}