blob: 7786fe6e42546d8db9489c3eced24b1e5e8a0966 [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
18import android.app.AlarmManager;
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
Jason Monk9ced3cd2013-08-12 16:42:38 -040021import android.content.ComponentName;
Jason Monk602b2322013-07-03 17:04:33 -040022import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
Jason Monk9ced3cd2013-08-12 16:42:38 -040026import android.content.ServiceConnection;
Jason Monk6f8a68f2013-08-23 19:21:25 -040027import android.net.Proxy;
Jason Monk602b2322013-07-03 17:04:33 -040028import android.net.ProxyProperties;
Jason Monk6f8a68f2013-08-23 19:21:25 -040029import android.os.Binder;
Jason Monkdecd2952013-10-10 14:02:51 -040030import android.os.Handler;
Jason Monk9ced3cd2013-08-12 16:42:38 -040031import android.os.IBinder;
Jason Monk602b2322013-07-03 17:04:33 -040032import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.SystemClock;
35import android.os.SystemProperties;
Jason Monk6f8a68f2013-08-23 19:21:25 -040036import android.os.UserHandle;
Jason Monk602b2322013-07-03 17:04:33 -040037import android.provider.Settings;
38import android.text.TextUtils;
39import 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;
Jason Monk9ced3cd2013-08-12 16:42:38 -040045import com.android.server.IoThread;
Jason Monk602b2322013-07-03 17:04:33 -040046
Jason Monk9ced3cd2013-08-12 16:42:38 -040047import libcore.io.Streams;
Jason Monk602b2322013-07-03 17:04:33 -040048
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 {
Jason Monkda205a72013-08-21 15:57:30 -040057 public static final String PAC_PACKAGE = "com.android.pacprocessor";
58 public static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
59 public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040060
Jason Monkda205a72013-08-21 15:57:30 -040061 public static final String PROXY_PACKAGE = "com.android.proxyhandler";
62 public 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;
72
73 /** Keep these values up-to-date with ProxyService.java */
74 public static final String KEY_PROXY = "keyProxy";
75 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040076 @GuardedBy("mProxyLock")
77 private String mPacUrl;
Jason Monk602b2322013-07-03 17:04:33 -040078
79 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040080 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040081 private IProxyService mProxyService;
82 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040083 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040084 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040085 private Context mContext;
86
87 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040088 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040089
Jason Monkd4434792013-09-23 13:32:39 -040090 private boolean mHasSentBroadcast;
91 private boolean mHasDownloaded;
92
Jason Monkdecd2952013-10-10 14:02:51 -040093 private Handler mConnectivityHandler;
94 private int mProxyMessage;
95
Jason Monk9ced3cd2013-08-12 16:42:38 -040096 /**
97 * Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac.
98 */
99 private final Object mProxyLock = new Object();
100
101 private Runnable mPacDownloader = new Runnable() {
102 @Override
103 public void run() {
104 String file;
105 synchronized (mProxyLock) {
106 if (mPacUrl == null) return;
107 try {
108 file = get(mPacUrl);
109 } catch (IOException ioe) {
110 file = null;
111 Log.w(TAG, "Failed to load PAC file: " + ioe);
112 }
113 }
114 if (file != null) {
115 synchronized (mProxyLock) {
116 if (!file.equals(mCurrentPac)) {
117 setCurrentProxyScript(file);
118 }
119 }
Jason Monkd4434792013-09-23 13:32:39 -0400120 mHasDownloaded = true;
121 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400122 longSchedule();
123 } else {
124 reschedule();
125 }
126 }
127 };
128
Jason Monk602b2322013-07-03 17:04:33 -0400129 class PacRefreshIntentReceiver extends BroadcastReceiver {
130 public void onReceive(Context context, Intent intent) {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400131 IoThread.getHandler().post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400132 }
133 }
134
Jason Monkdecd2952013-10-10 14:02:51 -0400135 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400136 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400137 mLastPort = -1;
Jason Monk602b2322013-07-03 17:04:33 -0400138
139 mPacRefreshIntent = PendingIntent.getBroadcast(
140 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
141 context.registerReceiver(new PacRefreshIntentReceiver(),
142 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400143 mConnectivityHandler = handler;
144 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400145 }
146
147 private AlarmManager getAlarmManager() {
148 if (mAlarmManager == null) {
149 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
150 }
151 return mAlarmManager;
152 }
153
Jason Monk6f8a68f2013-08-23 19:21:25 -0400154 /**
155 * Updates the PAC Manager with current Proxy information. This is called by
156 * the ConnectivityService directly before a broadcast takes place to allow
157 * the PacManager to indicate that the broadcast should not be sent and the
158 * PacManager will trigger a new broadcast when it is ready.
159 *
160 * @param proxy Proxy information that is about to be broadcast.
161 * @return Returns true when the broadcast should not be sent
162 */
163 public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
Jason Monk602b2322013-07-03 17:04:33 -0400164 if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400165 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400166 // Allow to send broadcast, nothing to do.
167 return false;
168 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400169 synchronized (mProxyLock) {
Jason Monk602b2322013-07-03 17:04:33 -0400170 mPacUrl = proxy.getPacFileUrl();
Jason Monk602b2322013-07-03 17:04:33 -0400171 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400172 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400173 mHasSentBroadcast = false;
174 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400175 getAlarmManager().cancel(mPacRefreshIntent);
176 bind();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400177 return true;
Jason Monk602b2322013-07-03 17:04:33 -0400178 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400179 getAlarmManager().cancel(mPacRefreshIntent);
180 synchronized (mProxyLock) {
181 mPacUrl = null;
182 mCurrentPac = null;
183 if (mProxyService != null) {
184 try {
185 mProxyService.stopPacSystem();
186 } catch (RemoteException e) {
187 Log.w(TAG, "Failed to stop PAC service", e);
188 } finally {
189 unbind();
190 }
191 }
Jason Monk602b2322013-07-03 17:04:33 -0400192 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400193 return false;
Jason Monk602b2322013-07-03 17:04:33 -0400194 }
195 }
196
197 /**
198 * Does a post and reports back the status code.
199 *
200 * @throws IOException
201 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400202 private static String get(String urlString) throws IOException {
Jason Monk602b2322013-07-03 17:04:33 -0400203 URL url = new URL(urlString);
204 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400205 return new String(Streams.readFully(urlConnection.getInputStream()));
Jason Monk602b2322013-07-03 17:04:33 -0400206 }
207
208 private int getNextDelay(int currentDelay) {
209 if (++currentDelay > DELAY_4) {
210 return DELAY_4;
211 }
212 return currentDelay;
213 }
214
215 private void longSchedule() {
216 mCurrentDelay = DELAY_1;
217 setDownloadIn(DELAY_LONG);
218 }
219
220 private void reschedule() {
221 mCurrentDelay = getNextDelay(mCurrentDelay);
222 setDownloadIn(mCurrentDelay);
223 }
224
225 private String getPacChangeDelay() {
226 final ContentResolver cr = mContext.getContentResolver();
227
228 /** Check system properties for the default value then use secure settings value, if any. */
229 String defaultDelay = SystemProperties.get(
230 "conn." + Settings.Global.PAC_CHANGE_DELAY,
231 DEFAULT_DELAYS);
232 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
233 return (val == null) ? defaultDelay : val;
234 }
235
236 private long getDownloadDelay(int delayIndex) {
237 String[] list = getPacChangeDelay().split(" ");
238 if (delayIndex < list.length) {
239 return Long.parseLong(list[delayIndex]);
240 }
241 return 0;
242 }
243
244 private void setDownloadIn(int delayIndex) {
245 long delay = getDownloadDelay(delayIndex);
246 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
247 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
248 }
249
250 private boolean setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700251 if (mProxyService == null) {
252 Log.e(TAG, "setCurrentProxyScript: no proxy service");
253 return false;
254 }
Jason Monk602b2322013-07-03 17:04:33 -0400255 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400256 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400257 mCurrentPac = script;
258 } catch (RemoteException e) {
259 Log.e(TAG, "Unable to set PAC file", e);
260 }
261 return true;
262 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400263
264 private void bind() {
265 if (mContext == null) {
266 Log.e(TAG, "No context for binding");
267 return;
268 }
269 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400270 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400271 // Already bound no need to bind again.
Jason Monkbc018d82013-09-17 16:37:38 -0400272 if ((mProxyConnection != null) && (mConnection != null)) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400273 if (mLastPort != -1) {
274 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
275 } else {
276 Log.e(TAG, "Received invalid port from Local Proxy,"
277 + " PAC will not be operational");
278 }
279 return;
280 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400281 mConnection = new ServiceConnection() {
282 @Override
283 public void onServiceDisconnected(ComponentName component) {
284 synchronized (mProxyLock) {
285 mProxyService = null;
286 }
287 }
288
289 @Override
290 public void onServiceConnected(ComponentName component, IBinder binder) {
291 synchronized (mProxyLock) {
292 try {
Jason Monkda205a72013-08-21 15:57:30 -0400293 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400294 + binder.getInterfaceDescriptor());
295 } catch (RemoteException e1) {
296 Log.e(TAG, "Remote Exception", e1);
297 }
Jason Monkda205a72013-08-21 15:57:30 -0400298 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400299 mProxyService = IProxyService.Stub.asInterface(binder);
300 if (mProxyService == null) {
301 Log.e(TAG, "No proxy service");
302 } else {
303 try {
304 mProxyService.startPacSystem();
305 } catch (RemoteException e) {
306 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
307 }
308 IoThread.getHandler().post(mPacDownloader);
309 }
310 }
311 }
312 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400313 mContext.bindService(intent, mConnection,
314 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400315
316 intent = new Intent();
317 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
318 mProxyConnection = new ServiceConnection() {
319 @Override
320 public void onServiceDisconnected(ComponentName component) {
321 }
322
323 @Override
324 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400325 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
326 if (callbackService != null) {
327 try {
328 callbackService.getProxyPort(new IProxyPortListener.Stub() {
329 @Override
330 public void setProxyPort(int port) throws RemoteException {
Jason Monkd4434792013-09-23 13:32:39 -0400331 if (mLastPort != -1) {
332 // Always need to send if port changed
333 mHasSentBroadcast = false;
334 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400335 mLastPort = port;
336 if (port != -1) {
337 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400338 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400339 } else {
340 Log.e(TAG, "Received invalid port from Local Proxy,"
341 + " PAC will not be operational");
342 }
343 }
344 });
345 } catch (RemoteException e) {
346 e.printStackTrace();
347 }
348 }
Jason Monkda205a72013-08-21 15:57:30 -0400349 }
350 };
351 mContext.bindService(intent, mProxyConnection,
352 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400353 }
354
355 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400356 if (mConnection != null) {
357 mContext.unbindService(mConnection);
358 mConnection = null;
359 }
360 if (mProxyConnection != null) {
361 mContext.unbindService(mProxyConnection);
362 mProxyConnection = null;
363 }
364 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400365 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400366 }
367
368 private void sendPacBroadcast(ProxyProperties proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400369 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400370 }
Jason Monkd4434792013-09-23 13:32:39 -0400371
372 private synchronized void sendProxyIfNeeded() {
373 if (!mHasDownloaded || (mLastPort == -1)) {
374 return;
375 }
376 if (!mHasSentBroadcast) {
377 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
378 mHasSentBroadcast = true;
379 }
380 }
Jason Monk602b2322013-07-03 17:04:33 -0400381}