19ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk/**
29ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Copyright (c) 2013, The Android Open Source Project
39ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
49ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Licensed under the Apache License, Version 2.0 (the "License");
59ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * you may not use this file except in compliance with the License.
69ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * You may obtain a copy of the License at
79ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
89ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *     http://www.apache.org/licenses/LICENSE-2.0
99ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk *
109ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * Unless required by applicable law or agreed to in writing, software
119ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * distributed under the License is distributed on an "AS IS" BASIS,
129ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * See the License for the specific language governing permissions and
149ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk * limitations under the License.
159ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk */
16602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpackage com.android.server.connectivity;
17602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
184010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hironoimport android.annotation.WorkerThread;
19602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.app.AlarmManager;
20602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.app.PendingIntent;
21602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.content.BroadcastReceiver;
229ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkimport android.content.ComponentName;
23602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.content.ContentResolver;
24602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.content.Context;
25602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.content.Intent;
26602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.content.IntentFilter;
279ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkimport android.content.ServiceConnection;
28207900c23b26d0df9ab28c709db4a1007d7d7904Jason Monkimport android.net.ProxyInfo;
29619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkeyimport android.net.TrafficStats;
3083520b95124e0fcaaf3154a7a267f6be0205bc74Jason Monkimport android.net.Uri;
31decd295b1371238c97c170226c6145948492eda1Jason Monkimport android.os.Handler;
327d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensenimport android.os.HandlerThread;
339ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkimport android.os.IBinder;
34602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.os.RemoteException;
35602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.os.ServiceManager;
36602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.os.SystemClock;
37602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.os.SystemProperties;
38602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.provider.Settings;
39602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.util.Log;
40602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
419ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkimport com.android.internal.annotations.GuardedBy;
426f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport com.android.net.IProxyCallback;
436f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport com.android.net.IProxyPortListener;
44602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport com.android.net.IProxyService;
45602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
469ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkimport libcore.io.Streams;
47602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
487d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensenimport java.io.ByteArrayOutputStream;
49602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.IOException;
50602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URL;
51602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URLConnection;
52602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
53602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk/**
54602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk * @hide
55602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk */
569ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monkpublic class PacManager {
57da205a749fadb3a87357d9bd607f094c7717764aJason Monk    public static final String PAC_PACKAGE = "com.android.pacprocessor";
58da205a749fadb3a87357d9bd607f094c7717764aJason Monk    public static final String PAC_SERVICE = "com.android.pacprocessor.PacService";
59da205a749fadb3a87357d9bd607f094c7717764aJason Monk    public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService";
60602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
61da205a749fadb3a87357d9bd607f094c7717764aJason Monk    public static final String PROXY_PACKAGE = "com.android.proxyhandler";
62da205a749fadb3a87357d9bd607f094c7717764aJason Monk    public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
63602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
649ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private static final String TAG = "PacManager";
65602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
66602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH";
67602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
68602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final String DEFAULT_DELAYS = "8 32 120 14400 43200";
69602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final int DELAY_1 = 0;
70602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final int DELAY_4 = 3;
71602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final int DELAY_LONG = 4;
727d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen    private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
73602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
74602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    /** Keep these values up-to-date with ProxyService.java */
75602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public static final String KEY_PROXY = "keyProxy";
76602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private String mCurrentPac;
779ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    @GuardedBy("mProxyLock")
784010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono    private volatile Uri mPacUrl = Uri.EMPTY;
79602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
80602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private AlarmManager mAlarmManager;
819ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    @GuardedBy("mProxyLock")
82602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private IProxyService mProxyService;
83602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private PendingIntent mPacRefreshIntent;
849ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private ServiceConnection mConnection;
85da205a749fadb3a87357d9bd607f094c7717764aJason Monk    private ServiceConnection mProxyConnection;
86602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private Context mContext;
87602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
88602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private int mCurrentDelay;
896f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    private int mLastPort;
90602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
914010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono    private volatile boolean mHasSentBroadcast;
924010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono    private volatile boolean mHasDownloaded;
93d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk
94decd295b1371238c97c170226c6145948492eda1Jason Monk    private Handler mConnectivityHandler;
95decd295b1371238c97c170226c6145948492eda1Jason Monk    private int mProxyMessage;
96decd295b1371238c97c170226c6145948492eda1Jason Monk
979ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    /**
984010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono     * Used for locking when setting mProxyService and all references to mCurrentPac.
999ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk     */
1009ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private final Object mProxyLock = new Object();
1019ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk
1024010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono    /**
1034010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono     * Runnable to download PAC script.
1044010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono     * The behavior relies on the assamption it always run on mNetThread to guarantee that the
1054010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono     * latest data fetched from mPacUrl is stored in mProxyService.
1064010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono     */
1079ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private Runnable mPacDownloader = new Runnable() {
1089ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        @Override
1094010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono        @WorkerThread
1109ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        public void run() {
1119ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            String file;
1124010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            final Uri pacUrl = mPacUrl;
1134010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            if (Uri.EMPTY.equals(pacUrl)) return;
1144010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
1154010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            try {
1164010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono                file = get(pacUrl);
1174010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            } catch (IOException ioe) {
1184010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono                file = null;
1194010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono                Log.w(TAG, "Failed to load PAC file: " + ioe);
1204010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            } finally {
1214010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono                TrafficStats.setThreadStatsTag(oldTag);
1229ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            }
1239ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            if (file != null) {
1249ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                synchronized (mProxyLock) {
1259ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    if (!file.equals(mCurrentPac)) {
1269ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        setCurrentProxyScript(file);
1279ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    }
1289ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                }
129d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                mHasDownloaded = true;
130d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                sendProxyIfNeeded();
1319ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                longSchedule();
1329ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            } else {
1339ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                reschedule();
1349ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            }
1359ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        }
1369ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    };
1379ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk
1387d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen    private final HandlerThread mNetThread = new HandlerThread("android.pacmanager",
1397d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            android.os.Process.THREAD_PRIORITY_DEFAULT);
1407d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen    private final Handler mNetThreadHandler;
1417d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen
142602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    class PacRefreshIntentReceiver extends BroadcastReceiver {
143602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        public void onReceive(Context context, Intent intent) {
1447d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            mNetThreadHandler.post(mPacDownloader);
145602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
146602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
147602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
148decd295b1371238c97c170226c6145948492eda1Jason Monk    public PacManager(Context context, Handler handler, int proxyMessage) {
149602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mContext = context;
1506f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        mLastPort = -1;
1517d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        mNetThread.start();
1527d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        mNetThreadHandler = new Handler(mNetThread.getLooper());
153602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
154602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mPacRefreshIntent = PendingIntent.getBroadcast(
155602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                context, 0, new Intent(ACTION_PAC_REFRESH), 0);
156602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        context.registerReceiver(new PacRefreshIntentReceiver(),
157602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                new IntentFilter(ACTION_PAC_REFRESH));
158decd295b1371238c97c170226c6145948492eda1Jason Monk        mConnectivityHandler = handler;
159decd295b1371238c97c170226c6145948492eda1Jason Monk        mProxyMessage = proxyMessage;
160602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
161602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
162602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private AlarmManager getAlarmManager() {
163602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        if (mAlarmManager == null) {
164602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
165602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
166602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return mAlarmManager;
167602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
168602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
1696f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    /**
1706f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * Updates the PAC Manager with current Proxy information. This is called by
1716f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * the ConnectivityService directly before a broadcast takes place to allow
1726f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * the PacManager to indicate that the broadcast should not be sent and the
1736f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * PacManager will trigger a new broadcast when it is ready.
1746f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     *
1756f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * @param proxy Proxy information that is about to be broadcast.
1766f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     * @return Returns true when the broadcast should not be sent
1776f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk     */
178207900c23b26d0df9ab28c709db4a1007d7d7904Jason Monk    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
17983520b95124e0fcaaf3154a7a267f6be0205bc74Jason Monk        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
18090760c8fa36574d6cc9224046758317ca6f640bfJason Monk            if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
181decd295b1371238c97c170226c6145948492eda1Jason Monk                // Allow to send broadcast, nothing to do.
182decd295b1371238c97c170226c6145948492eda1Jason Monk                return false;
183decd295b1371238c97c170226c6145948492eda1Jason Monk            }
1844010fe49486338f9aa31aba238c60d1837ce1e74Daichi Hirono            mPacUrl = proxy.getPacFileUrl();
1859ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            mCurrentDelay = DELAY_1;
186d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk            mHasSentBroadcast = false;
187d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk            mHasDownloaded = false;
1889ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            getAlarmManager().cancel(mPacRefreshIntent);
1899ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            bind();
1906f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            return true;
191602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        } else {
1929ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            getAlarmManager().cancel(mPacRefreshIntent);
1939ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            synchronized (mProxyLock) {
194f6b46cb8b108cc8feee8ae68b979271fd6f72bf5Jason Monk                mPacUrl = Uri.EMPTY;
1959ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                mCurrentPac = null;
1969ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                if (mProxyService != null) {
1979ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    try {
1989ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        mProxyService.stopPacSystem();
1999ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    } catch (RemoteException e) {
2009ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        Log.w(TAG, "Failed to stop PAC service", e);
2019ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    } finally {
2029ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        unbind();
2039ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    }
2049ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                }
205602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
2066f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            return false;
207602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
208602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
209602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
210602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    /**
211602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk     * Does a post and reports back the status code.
212602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk     *
213602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk     * @throws IOException
214602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk     */
21583520b95124e0fcaaf3154a7a267f6be0205bc74Jason Monk    private static String get(Uri pacUri) throws IOException {
21683520b95124e0fcaaf3154a7a267f6be0205bc74Jason Monk        URL url = new URL(pacUri.toString());
217602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
2187d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        long contentLength = -1;
2197d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        try {
2207d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));
2217d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        } catch (NumberFormatException e) {
2227d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            // Ignore
2237d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        }
2247d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        if (contentLength > MAX_PAC_SIZE) {
2257d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            throw new IOException("PAC too big: " + contentLength + " bytes");
2267d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        }
2277d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
2287d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        byte[] buffer = new byte[1024];
2297d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        int count;
2307d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        while ((count = urlConnection.getInputStream().read(buffer)) != -1) {
2317d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            bytes.write(buffer, 0, count);
2327d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            if (bytes.size() > MAX_PAC_SIZE) {
2337d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen                throw new IOException("PAC too big");
2347d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen            }
2357d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        }
2367d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen        return bytes.toString();
237602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
238602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
239602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private int getNextDelay(int currentDelay) {
240602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk       if (++currentDelay > DELAY_4) {
241602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk           return DELAY_4;
242602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk       }
243602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk       return currentDelay;
244602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
245602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
246602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private void longSchedule() {
247602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mCurrentDelay = DELAY_1;
248602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        setDownloadIn(DELAY_LONG);
249602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
250602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
251602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private void reschedule() {
252602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mCurrentDelay = getNextDelay(mCurrentDelay);
253602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        setDownloadIn(mCurrentDelay);
254602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
255602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
256602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private String getPacChangeDelay() {
257602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        final ContentResolver cr = mContext.getContentResolver();
258602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
259602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        /** Check system properties for the default value then use secure settings value, if any. */
260602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String defaultDelay = SystemProperties.get(
261602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                "conn." + Settings.Global.PAC_CHANGE_DELAY,
262602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                DEFAULT_DELAYS);
263602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String val = Settings.Global.getString(cr, Settings.Global.PAC_CHANGE_DELAY);
264602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return (val == null) ? defaultDelay : val;
265602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
266602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
267602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private long getDownloadDelay(int delayIndex) {
268602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        String[] list = getPacChangeDelay().split(" ");
269602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        if (delayIndex < list.length) {
270602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            return Long.parseLong(list[delayIndex]);
271602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
272602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return 0;
273602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
274602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
275602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private void setDownloadIn(int delayIndex) {
276602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        long delay = getDownloadDelay(delayIndex);
277602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        long timeTillTrigger = 1000 * delay + SystemClock.elapsedRealtime();
278602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
279602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
280602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
281602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private boolean setCurrentProxyScript(String script) {
282a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        if (mProxyService == null) {
283a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville            Log.e(TAG, "setCurrentProxyScript: no proxy service");
284a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville            return false;
285a48ad8bd858d6ffe77838a282dbf71e01967957cWink Saville        }
286602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        try {
2879ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            mProxyService.setPacFile(script);
288602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            mCurrentPac = script;
289602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        } catch (RemoteException e) {
290602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            Log.e(TAG, "Unable to set PAC file", e);
291602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
292602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        return true;
293602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
2949ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk
2959ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private void bind() {
2969ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        if (mContext == null) {
2979ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            Log.e(TAG, "No context for binding");
2989ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            return;
2999ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        }
3009ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        Intent intent = new Intent();
301da205a749fadb3a87357d9bd607f094c7717764aJason Monk        intent.setClassName(PAC_PACKAGE, PAC_SERVICE);
302bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        if ((mProxyConnection != null) && (mConnection != null)) {
3034bf1d218576fa5721bab589cd5945dbe49f0d117Jason Monk            // Already bound no need to bind again, just download the new file.
304baae57ab24db7d3b0bf7f84c05954173f1ee15fePaul Jensen            mNetThreadHandler.post(mPacDownloader);
3056f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            return;
3066f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        }
3079ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        mConnection = new ServiceConnection() {
3089ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            @Override
3099ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            public void onServiceDisconnected(ComponentName component) {
3109ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                synchronized (mProxyLock) {
3119ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    mProxyService = null;
3129ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                }
3139ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            }
3149ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk
3159ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            @Override
3169ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            public void onServiceConnected(ComponentName component, IBinder binder) {
3179ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                synchronized (mProxyLock) {
3189ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    try {
319da205a749fadb3a87357d9bd607f094c7717764aJason Monk                        Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
3209ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                                + binder.getInterfaceDescriptor());
3219ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    } catch (RemoteException e1) {
3229ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        Log.e(TAG, "Remote Exception", e1);
3239ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    }
324da205a749fadb3a87357d9bd607f094c7717764aJason Monk                    ServiceManager.addService(PAC_SERVICE_NAME, binder);
3259ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    mProxyService = IProxyService.Stub.asInterface(binder);
3269ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    if (mProxyService == null) {
3279ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        Log.e(TAG, "No proxy service");
3289ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    } else {
3299ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        try {
3309ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                            mProxyService.startPacSystem();
3319ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        } catch (RemoteException e) {
3329ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                            Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
3339ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                        }
3347d2198b586bcfbf96fb627021a0eb85d32829cc0Paul Jensen                        mNetThreadHandler.post(mPacDownloader);
3359ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                    }
3369ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                }
3379ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk            }
3389ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        };
3399ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk        mContext.bindService(intent, mConnection,
3409ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
341da205a749fadb3a87357d9bd607f094c7717764aJason Monk
342da205a749fadb3a87357d9bd607f094c7717764aJason Monk        intent = new Intent();
343da205a749fadb3a87357d9bd607f094c7717764aJason Monk        intent.setClassName(PROXY_PACKAGE, PROXY_SERVICE);
344da205a749fadb3a87357d9bd607f094c7717764aJason Monk        mProxyConnection = new ServiceConnection() {
345da205a749fadb3a87357d9bd607f094c7717764aJason Monk            @Override
346da205a749fadb3a87357d9bd607f094c7717764aJason Monk            public void onServiceDisconnected(ComponentName component) {
347da205a749fadb3a87357d9bd607f094c7717764aJason Monk            }
348da205a749fadb3a87357d9bd607f094c7717764aJason Monk
349da205a749fadb3a87357d9bd607f094c7717764aJason Monk            @Override
350da205a749fadb3a87357d9bd607f094c7717764aJason Monk            public void onServiceConnected(ComponentName component, IBinder binder) {
3516f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                IProxyCallback callbackService = IProxyCallback.Stub.asInterface(binder);
3526f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                if (callbackService != null) {
3536f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    try {
3546f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                        callbackService.getProxyPort(new IProxyPortListener.Stub() {
3556f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                            @Override
3566f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                            public void setProxyPort(int port) throws RemoteException {
357d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                                if (mLastPort != -1) {
358d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                                    // Always need to send if port changed
359d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                                    mHasSentBroadcast = false;
360d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                                }
3616f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                mLastPort = port;
3626f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                if (port != -1) {
3636f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                    Log.d(TAG, "Local proxy is bound on " + port);
364d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk                                    sendProxyIfNeeded();
3656f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                } else {
3666f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                    Log.e(TAG, "Received invalid port from Local Proxy,"
3676f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                            + " PAC will not be operational");
3686f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                                }
3696f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                            }
3706f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                        });
3716f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    } catch (RemoteException e) {
3726f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                        e.printStackTrace();
3736f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    }
3746f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                }
375da205a749fadb3a87357d9bd607f094c7717764aJason Monk            }
376da205a749fadb3a87357d9bd607f094c7717764aJason Monk        };
377da205a749fadb3a87357d9bd607f094c7717764aJason Monk        mContext.bindService(intent, mProxyConnection,
378da205a749fadb3a87357d9bd607f094c7717764aJason Monk                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
3799ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    }
3809ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk
3819ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    private void unbind() {
382bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        if (mConnection != null) {
383bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk            mContext.unbindService(mConnection);
384bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk            mConnection = null;
385bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        }
386bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        if (mProxyConnection != null) {
387bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk            mContext.unbindService(mProxyConnection);
388bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk            mProxyConnection = null;
389bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        }
390bc018d89c27cdd93d1c0222a6e27965455974b9eJason Monk        mProxyService = null;
391d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk        mLastPort = -1;
3926f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    }
3936f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk
394207900c23b26d0df9ab28c709db4a1007d7d7904Jason Monk    private void sendPacBroadcast(ProxyInfo proxy) {
395decd295b1371238c97c170226c6145948492eda1Jason Monk        mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
3969ced3cd9d6ea414523051ec872fffc68f5fdbf08Jason Monk    }
397d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk
398d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk    private synchronized void sendProxyIfNeeded() {
399d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk        if (!mHasDownloaded || (mLastPort == -1)) {
400d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk            return;
401d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk        }
402d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk        if (!mHasSentBroadcast) {
403207900c23b26d0df9ab28c709db4a1007d7d7904Jason Monk            sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
404d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk            mHasSentBroadcast = true;
405d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk        }
406d443479a45279f8f52daf4034698f2ab3c3e62f1Jason Monk    }
407602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk}
408