blob: 8815d0fec0dee2aec01b7fee1f3397cf33da1d52 [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 Monk602b2322013-07-03 17:04:33 -040027import android.net.ProxyProperties;
Jason Monkdecd2952013-10-10 14:02:51 -040028import android.os.Handler;
Jason Monk9ced3cd2013-08-12 16:42:38 -040029import android.os.IBinder;
Jason Monk602b2322013-07-03 17:04:33 -040030import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.SystemClock;
33import android.os.SystemProperties;
34import android.provider.Settings;
35import android.text.TextUtils;
36import android.util.Log;
37
Jason Monk9ced3cd2013-08-12 16:42:38 -040038import com.android.internal.annotations.GuardedBy;
Jason Monk6f8a68f2013-08-23 19:21:25 -040039import com.android.net.IProxyCallback;
40import com.android.net.IProxyPortListener;
Jason Monk602b2322013-07-03 17:04:33 -040041import com.android.net.IProxyService;
Jason Monk9ced3cd2013-08-12 16:42:38 -040042import com.android.server.IoThread;
Jason Monk602b2322013-07-03 17:04:33 -040043
Jason Monk9ced3cd2013-08-12 16:42:38 -040044import libcore.io.Streams;
Jason Monk602b2322013-07-03 17:04:33 -040045
Jason Monk602b2322013-07-03 17:04:33 -040046import java.io.IOException;
Jason Monk602b2322013-07-03 17:04:33 -040047import java.net.URL;
48import java.net.URLConnection;
49
50/**
51 * @hide
52 */
Jason Monk9ced3cd2013-08-12 16:42:38 -040053public class PacManager {
Jason Monkda205a72013-08-21 15:57:30 -040054 public static final String PAC_PACKAGE = "com.android.pacprocessor";
55 public static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
56 public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040057
Jason Monkda205a72013-08-21 15:57:30 -040058 public static final String PROXY_PACKAGE = "com.android.proxyhandler";
59 public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
Jason Monk602b2322013-07-03 17:04:33 -040060
Jason Monk9ced3cd2013-08-12 16:42:38 -040061 private static final String TAG = "PacManager";
Jason Monk602b2322013-07-03 17:04:33 -040062
63 private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
64
65 private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
66 private static final int DELAY_1 = 0;
67 private static final int DELAY_4 = 3;
68 private static final int DELAY_LONG = 4;
69
70 /** Keep these values up-to-date with ProxyService.java */
71 public static final String KEY_PROXY = "keyProxy";
72 private String mCurrentPac;
Jason Monk9ced3cd2013-08-12 16:42:38 -040073 @GuardedBy("mProxyLock")
74 private String mPacUrl;
Jason Monk602b2322013-07-03 17:04:33 -040075
76 private AlarmManager mAlarmManager;
Jason Monk9ced3cd2013-08-12 16:42:38 -040077 @GuardedBy("mProxyLock")
Jason Monk602b2322013-07-03 17:04:33 -040078 private IProxyService mProxyService;
79 private PendingIntent mPacRefreshIntent;
Jason Monk9ced3cd2013-08-12 16:42:38 -040080 private ServiceConnection mConnection;
Jason Monkda205a72013-08-21 15:57:30 -040081 private ServiceConnection mProxyConnection;
Jason Monk602b2322013-07-03 17:04:33 -040082 private Context mContext;
83
84 private int mCurrentDelay;
Jason Monk6f8a68f2013-08-23 19:21:25 -040085 private int mLastPort;
Jason Monk602b2322013-07-03 17:04:33 -040086
Jason Monkd4434792013-09-23 13:32:39 -040087 private boolean mHasSentBroadcast;
88 private boolean mHasDownloaded;
89
Jason Monkdecd2952013-10-10 14:02:51 -040090 private Handler mConnectivityHandler;
91 private int mProxyMessage;
92
Jason Monk9ced3cd2013-08-12 16:42:38 -040093 /**
94 * Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac.
95 */
96 private final Object mProxyLock = new Object();
97
98 private Runnable mPacDownloader = new Runnable() {
99 @Override
100 public void run() {
101 String file;
102 synchronized (mProxyLock) {
103 if (mPacUrl == null) return;
104 try {
105 file = get(mPacUrl);
106 } catch (IOException ioe) {
107 file = null;
108 Log.w(TAG, "Failed to load PAC file: " + ioe);
109 }
110 }
111 if (file != null) {
112 synchronized (mProxyLock) {
113 if (!file.equals(mCurrentPac)) {
114 setCurrentProxyScript(file);
115 }
116 }
Jason Monkd4434792013-09-23 13:32:39 -0400117 mHasDownloaded = true;
118 sendProxyIfNeeded();
Jason Monk9ced3cd2013-08-12 16:42:38 -0400119 longSchedule();
120 } else {
121 reschedule();
122 }
123 }
124 };
125
Jason Monk602b2322013-07-03 17:04:33 -0400126 class PacRefreshIntentReceiver extends BroadcastReceiver {
127 public void onReceive(Context context, Intent intent) {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400128 IoThread.getHandler().post(mPacDownloader);
Jason Monk602b2322013-07-03 17:04:33 -0400129 }
130 }
131
Jason Monkdecd2952013-10-10 14:02:51 -0400132 public PacManager(Context context, Handler handler, int proxyMessage) {
Jason Monk602b2322013-07-03 17:04:33 -0400133 mContext = context;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400134 mLastPort = -1;
Jason Monk602b2322013-07-03 17:04:33 -0400135
136 mPacRefreshIntent = PendingIntent.getBroadcast(
137 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
138 context.registerReceiver(new PacRefreshIntentReceiver(),
139 new IntentFilter(ACTION_PAC_REFRESH));
Jason Monkdecd2952013-10-10 14:02:51 -0400140 mConnectivityHandler = handler;
141 mProxyMessage = proxyMessage;
Jason Monk602b2322013-07-03 17:04:33 -0400142 }
143
144 private AlarmManager getAlarmManager() {
145 if (mAlarmManager == null) {
146 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
147 }
148 return mAlarmManager;
149 }
150
Jason Monk6f8a68f2013-08-23 19:21:25 -0400151 /**
152 * Updates the PAC Manager with current Proxy information. This is called by
153 * the ConnectivityService directly before a broadcast takes place to allow
154 * the PacManager to indicate that the broadcast should not be sent and the
155 * PacManager will trigger a new broadcast when it is ready.
156 *
157 * @param proxy Proxy information that is about to be broadcast.
158 * @return Returns true when the broadcast should not be sent
159 */
160 public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
Jason Monk602b2322013-07-03 17:04:33 -0400161 if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
Jason Monk90760c82013-10-14 18:24:13 -0400162 if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
Jason Monkdecd2952013-10-10 14:02:51 -0400163 // Allow to send broadcast, nothing to do.
164 return false;
165 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400166 synchronized (mProxyLock) {
Jason Monk602b2322013-07-03 17:04:33 -0400167 mPacUrl = proxy.getPacFileUrl();
Jason Monk602b2322013-07-03 17:04:33 -0400168 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400169 mCurrentDelay = DELAY_1;
Jason Monkd4434792013-09-23 13:32:39 -0400170 mHasSentBroadcast = false;
171 mHasDownloaded = false;
Jason Monk9ced3cd2013-08-12 16:42:38 -0400172 getAlarmManager().cancel(mPacRefreshIntent);
173 bind();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400174 return true;
Jason Monk602b2322013-07-03 17:04:33 -0400175 } else {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400176 getAlarmManager().cancel(mPacRefreshIntent);
177 synchronized (mProxyLock) {
178 mPacUrl = null;
179 mCurrentPac = null;
180 if (mProxyService != null) {
181 try {
182 mProxyService.stopPacSystem();
183 } catch (RemoteException e) {
184 Log.w(TAG, "Failed to stop PAC service", e);
185 } finally {
186 unbind();
187 }
188 }
Jason Monk602b2322013-07-03 17:04:33 -0400189 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400190 return false;
Jason Monk602b2322013-07-03 17:04:33 -0400191 }
192 }
193
194 /**
195 * Does a post and reports back the status code.
196 *
197 * @throws IOException
198 */
Jason Monk9ced3cd2013-08-12 16:42:38 -0400199 private static String get(String urlString) throws IOException {
Jason Monk602b2322013-07-03 17:04:33 -0400200 URL url = new URL(urlString);
201 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400202 return new String(Streams.readFully(urlConnection.getInputStream()));
Jason Monk602b2322013-07-03 17:04:33 -0400203 }
204
205 private int getNextDelay(int currentDelay) {
206 if (++currentDelay > DELAY_4) {
207 return DELAY_4;
208 }
209 return currentDelay;
210 }
211
212 private void longSchedule() {
213 mCurrentDelay = DELAY_1;
214 setDownloadIn(DELAY_LONG);
215 }
216
217 private void reschedule() {
218 mCurrentDelay = getNextDelay(mCurrentDelay);
219 setDownloadIn(mCurrentDelay);
220 }
221
222 private String getPacChangeDelay() {
223 final ContentResolver cr = mContext.getContentResolver();
224
225 /** Check system properties for the default value then use secure settings value, if any. */
226 String defaultDelay = SystemProperties.get(
227 "conn." + Settings.Global.PAC_CHANGE_DELAY,
228 DEFAULT_DELAYS);
229 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
230 return (val == null) ? defaultDelay : val;
231 }
232
233 private long getDownloadDelay(int delayIndex) {
234 String[] list = getPacChangeDelay().split(" ");
235 if (delayIndex < list.length) {
236 return Long.parseLong(list[delayIndex]);
237 }
238 return 0;
239 }
240
241 private void setDownloadIn(int delayIndex) {
242 long delay = getDownloadDelay(delayIndex);
243 long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
244 getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
245 }
246
247 private boolean setCurrentProxyScript(String script) {
Wink Savillea48ad8b2013-08-10 11:22:31 -0700248 if (mProxyService == null) {
249 Log.e(TAG, "setCurrentProxyScript: no proxy service");
250 return false;
251 }
Jason Monk602b2322013-07-03 17:04:33 -0400252 try {
Jason Monk9ced3cd2013-08-12 16:42:38 -0400253 mProxyService.setPacFile(script);
Jason Monk602b2322013-07-03 17:04:33 -0400254 mCurrentPac = script;
255 } catch (RemoteException e) {
256 Log.e(TAG, "Unable to set PAC file", e);
257 }
258 return true;
259 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400260
261 private void bind() {
262 if (mContext == null) {
263 Log.e(TAG, "No context for binding");
264 return;
265 }
266 Intent intent = new Intent();
Jason Monkda205a72013-08-21 15:57:30 -0400267 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
Jason Monk6f8a68f2013-08-23 19:21:25 -0400268 // Already bound no need to bind again.
Jason Monkbc018d82013-09-17 16:37:38 -0400269 if ((mProxyConnection != null) && (mConnection != null)) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400270 if (mLastPort != -1) {
271 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
272 } else {
273 Log.e(TAG, "Received invalid port from Local Proxy,"
274 + " PAC will not be operational");
275 }
276 return;
277 }
Jason Monk9ced3cd2013-08-12 16:42:38 -0400278 mConnection = new ServiceConnection() {
279 @Override
280 public void onServiceDisconnected(ComponentName component) {
281 synchronized (mProxyLock) {
282 mProxyService = null;
283 }
284 }
285
286 @Override
287 public void onServiceConnected(ComponentName component, IBinder binder) {
288 synchronized (mProxyLock) {
289 try {
Jason Monkda205a72013-08-21 15:57:30 -0400290 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
Jason Monk9ced3cd2013-08-12 16:42:38 -0400291 + binder.getInterfaceDescriptor());
292 } catch (RemoteException e1) {
293 Log.e(TAG, "Remote Exception", e1);
294 }
Jason Monkda205a72013-08-21 15:57:30 -0400295 ServiceManager.addService(PAC_SERVICE_NAME, binder);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400296 mProxyService = IProxyService.Stub.asInterface(binder);
297 if (mProxyService == null) {
298 Log.e(TAG, "No proxy service");
299 } else {
300 try {
301 mProxyService.startPacSystem();
302 } catch (RemoteException e) {
303 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
304 }
305 IoThread.getHandler().post(mPacDownloader);
306 }
307 }
308 }
309 };
Jason Monk9ced3cd2013-08-12 16:42:38 -0400310 mContext.bindService(intent, mConnection,
311 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monkda205a72013-08-21 15:57:30 -0400312
313 intent = new Intent();
314 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
315 mProxyConnection = new ServiceConnection() {
316 @Override
317 public void onServiceDisconnected(ComponentName component) {
318 }
319
320 @Override
321 public void onServiceConnected(ComponentName component, IBinder binder) {
Jason Monk6f8a68f2013-08-23 19:21:25 -0400322 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
323 if (callbackService != null) {
324 try {
325 callbackService.getProxyPort(new IProxyPortListener.Stub() {
326 @Override
327 public void setProxyPort(int port) throws RemoteException {
Jason Monkd4434792013-09-23 13:32:39 -0400328 if (mLastPort != -1) {
329 // Always need to send if port changed
330 mHasSentBroadcast = false;
331 }
Jason Monk6f8a68f2013-08-23 19:21:25 -0400332 mLastPort = port;
333 if (port != -1) {
334 Log.d(TAG, "Local proxy is bound on " + port);
Jason Monkd4434792013-09-23 13:32:39 -0400335 sendProxyIfNeeded();
Jason Monk6f8a68f2013-08-23 19:21:25 -0400336 } else {
337 Log.e(TAG, "Received invalid port from Local Proxy,"
338 + " PAC will not be operational");
339 }
340 }
341 });
342 } catch (RemoteException e) {
343 e.printStackTrace();
344 }
345 }
Jason Monkda205a72013-08-21 15:57:30 -0400346 }
347 };
348 mContext.bindService(intent, mProxyConnection,
349 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
Jason Monk9ced3cd2013-08-12 16:42:38 -0400350 }
351
352 private void unbind() {
Jason Monkbc018d82013-09-17 16:37:38 -0400353 if (mConnection != null) {
354 mContext.unbindService(mConnection);
355 mConnection = null;
356 }
357 if (mProxyConnection != null) {
358 mContext.unbindService(mProxyConnection);
359 mProxyConnection = null;
360 }
361 mProxyService = null;
Jason Monkd4434792013-09-23 13:32:39 -0400362 mLastPort = -1;
Jason Monk6f8a68f2013-08-23 19:21:25 -0400363 }
364
365 private void sendPacBroadcast(ProxyProperties proxy) {
Jason Monkdecd2952013-10-10 14:02:51 -0400366 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
Jason Monk9ced3cd2013-08-12 16:42:38 -0400367 }
Jason Monkd4434792013-09-23 13:32:39 -0400368
369 private synchronized void sendProxyIfNeeded() {
370 if (!mHasDownloaded || (mLastPort == -1)) {
371 return;
372 }
373 if (!mHasSentBroadcast) {
374 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
375 mHasSentBroadcast = true;
376 }
377 }
Jason Monk602b2322013-07-03 17:04:33 -0400378}