2 * Copyright (c) 2013, The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.android.server.connectivity;
18 import android.app.AlarmManager;
19 import android.app.PendingIntent;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.ServiceConnection;
27 import android.net.Proxy;
28 import android.net.ProxyProperties;
29 import android.os.Binder;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.SystemProperties;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.text.TextUtils;
39 import android.util.Log;
41 import com.android.internal.annotations.GuardedBy;
42 import com.android.net.IProxyCallback;
43 import com.android.net.IProxyPortListener;
44 import com.android.net.IProxyService;
45 import com.android.server.IoThread;
47 import libcore.io.Streams;
49 import java.io.IOException;
51 import java.net.URLConnection;
56 public class PacManager {
57 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";
61 public static final String PROXY_PACKAGE = "com.android.proxyhandler";
62 public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
64 private static final String TAG = "PacManager";
66 private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
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;
73 /** Keep these values up-to-date with ProxyService.java */
74 public static final String KEY_PROXY = "keyProxy";
75 private String mCurrentPac;
76 @GuardedBy("mProxyLock")
77 private String mPacUrl;
79 private AlarmManager mAlarmManager;
80 @GuardedBy("mProxyLock")
81 private IProxyService mProxyService;
82 private PendingIntent mPacRefreshIntent;
83 private ServiceConnection mConnection;
84 private ServiceConnection mProxyConnection;
85 private Context mContext;
87 private int mCurrentDelay;
88 private int mLastPort;
90 private boolean mHasSentBroadcast;
91 private boolean mHasDownloaded;
93 private Handler mConnectivityHandler;
94 private int mProxyMessage;
97 * Used for locking when setting mProxyService and all references to mPacUrl or mCurrentPac.
99 private final Object mProxyLock = new Object();
101 private Runnable mPacDownloader = new Runnable() {
105 synchronized (mProxyLock) {
106 if (mPacUrl == null) return;
109 } catch (IOException ioe) {
111 Log.w(TAG, "Failed to load PAC file: " + ioe);
115 synchronized (mProxyLock) {
116 if (!file.equals(mCurrentPac)) {
117 setCurrentProxyScript(file);
120 mHasDownloaded = true;
129 class PacRefreshIntentReceiver extends BroadcastReceiver {
130 public void onReceive(Context context, Intent intent) {
131 IoThread.getHandler().post(mPacDownloader);
135 public PacManager(Context context, Handler handler, int proxyMessage) {
139 mPacRefreshIntent = PendingIntent.getBroadcast(
140 context, 0, new Intent(ACTION_PAC_REFRESH), 0);
141 context.registerReceiver(new PacRefreshIntentReceiver(),
142 new IntentFilter(ACTION_PAC_REFRESH));
143 mConnectivityHandler = handler;
144 mProxyMessage = proxyMessage;
147 private AlarmManager getAlarmManager() {
148 if (mAlarmManager == null) {
149 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
151 return mAlarmManager;
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.
160 * @param proxy Proxy information that is about to be broadcast.
161 * @return Returns true when the broadcast should not be sent
163 public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
164 if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
165 if (proxy.getPacFileUrl().equals(mPacUrl)) {
166 // Allow to send broadcast, nothing to do.
169 synchronized (mProxyLock) {
170 mPacUrl = proxy.getPacFileUrl();
172 mCurrentDelay = DELAY_1;
173 mHasSentBroadcast = false;
174 mHasDownloaded = false;
175 getAlarmManager().cancel(mPacRefreshIntent);
179 getAlarmManager().cancel(mPacRefreshIntent);
180 synchronized (mProxyLock) {
183 if (mProxyService != null) {
185 mProxyService.stopPacSystem();
186 } catch (RemoteException e) {
187 Log.w(TAG, "Failed to stop PAC service", e);
198 * Does a post and reports back the status code.
200 * @throws IOException
202 private static String get(String urlString) throws IOException {
203 URL url = new URL(urlString);
204 URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
205 return new String(Streams.readFully(urlConnection.getInputStream()));
208 private int getNextDelay(int currentDelay) {
209 if (++currentDelay > DELAY_4) {
215 private void longSchedule() {
216 mCurrentDelay = DELAY_1;
217 setDownloadIn(DELAY_LONG);
220 private void reschedule() {
221 mCurrentDelay = getNextDelay(mCurrentDelay);
222 setDownloadIn(mCurrentDelay);
225 private String getPacChangeDelay() {
226 final ContentResolver cr = mContext.getContentResolver();
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,
232 String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
233 return (val == null) ? defaultDelay : val;
236 private long getDownloadDelay(int delayIndex) {
237 String[] list = getPacChangeDelay().split(" ");
238 if (delayIndex < list.length) {
239 return Long.parseLong(list[delayIndex]);
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);
250 private boolean setCurrentProxyScript(String script) {
251 if (mProxyService == null) {
252 Log.e(TAG, "setCurrentProxyScript: no proxy service");
256 mProxyService.setPacFile(script);
257 mCurrentPac = script;
258 } catch (RemoteException e) {
259 Log.e(TAG, "Unable to set PAC file", e);
264 private void bind() {
265 if (mContext == null) {
266 Log.e(TAG, "No context for binding");
269 Intent intent = new Intent();
270 intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
271 // Already bound no need to bind again.
272 if ((mProxyConnection != null) && (mConnection != null)) {
273 if (mLastPort != -1) {
274 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
276 Log.e(TAG, "Received invalid port from Local Proxy,"
277 + " PAC will not be operational");
281 mConnection = new ServiceConnection() {
283 public void onServiceDisconnected(ComponentName component) {
284 synchronized (mProxyLock) {
285 mProxyService = null;
290 public void onServiceConnected(ComponentName component, IBinder binder) {
291 synchronized (mProxyLock) {
293 Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
294 + binder.getInterfaceDescriptor());
295 } catch (RemoteException e1) {
296 Log.e(TAG, "Remote Exception", e1);
298 ServiceManager.addService(PAC_SERVICE_NAME, binder);
299 mProxyService = IProxyService.Stub.asInterface(binder);
300 if (mProxyService == null) {
301 Log.e(TAG, "No proxy service");
304 mProxyService.startPacSystem();
305 } catch (RemoteException e) {
306 Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
308 IoThread.getHandler().post(mPacDownloader);
313 mContext.bindService(intent, mConnection,
314 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
316 intent = new Intent();
317 intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
318 mProxyConnection = new ServiceConnection() {
320 public void onServiceDisconnected(ComponentName component) {
324 public void onServiceConnected(ComponentName component, IBinder binder) {
325 IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
326 if (callbackService != null) {
328 callbackService.getProxyPort(new IProxyPortListener.Stub() {
330 public void setProxyPort(int port) throws RemoteException {
331 if (mLastPort != -1) {
332 // Always need to send if port changed
333 mHasSentBroadcast = false;
337 Log.d(TAG, "Local proxy is bound on " + port);
340 Log.e(TAG, "Received invalid port from Local Proxy,"
341 + " PAC will not be operational");
345 } catch (RemoteException e) {
351 mContext.bindService(intent, mProxyConnection,
352 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
355 private void unbind() {
356 if (mConnection != null) {
357 mContext.unbindService(mConnection);
360 if (mProxyConnection != null) {
361 mContext.unbindService(mProxyConnection);
362 mProxyConnection = null;
364 mProxyService = null;
368 private void sendPacBroadcast(ProxyProperties proxy) {
369 mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
372 private synchronized void sendProxyIfNeeded() {
373 if (!mHasDownloaded || (mLastPort == -1)) {
376 if (!mHasSentBroadcast) {
377 sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
378 mHasSentBroadcast = true;