OSDN Git Service

Merge "Fix SystemUI crash on devices with WiFi only" into klp-dev
[android-x86/frameworks-base.git] / services / java / com / android / server / connectivity / PacManager.java
1 /**
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  */
16 package com.android.server.connectivity;
17
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;
40
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;
46
47 import libcore.io.Streams;
48
49 import java.io.IOException;
50 import java.net.URL;
51 import java.net.URLConnection;
52
53 /**
54  * @hide
55  */
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";
60
61     public static final String PROXY_PACKAGE = "com.android.proxyhandler";
62     public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService";
63
64     private static final String TAG = "PacManager";
65
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;
76     @GuardedBy("mProxyLock")
77     private String mPacUrl;
78
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;
86
87     private int mCurrentDelay;
88     private int mLastPort;
89
90     private boolean mHasSentBroadcast;
91     private boolean mHasDownloaded;
92
93     private Handler mConnectivityHandler;
94     private int mProxyMessage;
95
96     /**
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                 }
120                 mHasDownloaded = true;
121                 sendProxyIfNeeded();
122                 longSchedule();
123             } else {
124                 reschedule();
125             }
126         }
127     };
128
129     class PacRefreshIntentReceiver extends BroadcastReceiver {
130         public void onReceive(Context context, Intent intent) {
131             IoThread.getHandler().post(mPacDownloader);
132         }
133     }
134
135     public PacManager(Context context, Handler handler, int proxyMessage) {
136         mContext = context;
137         mLastPort = -1;
138
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;
145     }
146
147     private AlarmManager getAlarmManager() {
148         if (mAlarmManager == null) {
149             mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
150         }
151         return mAlarmManager;
152     }
153
154     /**
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) {
164         if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
165             if (proxy.getPacFileUrl().equals(mPacUrl)) {
166                 // Allow to send broadcast, nothing to do.
167                 return false;
168             }
169             synchronized (mProxyLock) {
170                 mPacUrl = proxy.getPacFileUrl();
171             }
172             mCurrentDelay = DELAY_1;
173             mHasSentBroadcast = false;
174             mHasDownloaded = false;
175             getAlarmManager().cancel(mPacRefreshIntent);
176             bind();
177             return true;
178         } else {
179             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                 }
192             }
193             return false;
194         }
195     }
196
197     /**
198      * Does a post and reports back the status code.
199      *
200      * @throws IOException
201      */
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()));
206     }
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) {
251         if (mProxyService == null) {
252             Log.e(TAG, "setCurrentProxyScript: no proxy service");
253             return false;
254         }
255         try {
256             mProxyService.setPacFile(script);
257             mCurrentPac = script;
258         } catch (RemoteException e) {
259             Log.e(TAG, "Unable to set PAC file", e);
260         }
261         return true;
262     }
263
264     private void bind() {
265         if (mContext == null) {
266             Log.e(TAG, "No context for binding");
267             return;
268         }
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));
275             } else {
276                 Log.e(TAG, "Received invalid port from Local Proxy,"
277                         + " PAC will not be operational");
278             }
279             return;
280         }
281         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 {
293                         Log.d(TAG, "Adding service " + PAC_SERVICE_NAME + " "
294                                 + binder.getInterfaceDescriptor());
295                     } catch (RemoteException e1) {
296                         Log.e(TAG, "Remote Exception", e1);
297                     }
298                     ServiceManager.addService(PAC_SERVICE_NAME, binder);
299                     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         };
313         mContext.bindService(intent, mConnection,
314                 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
315
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) {
325                 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 {
331                                 if (mLastPort != -1) {
332                                     // Always need to send if port changed
333                                     mHasSentBroadcast = false;
334                                 }
335                                 mLastPort = port;
336                                 if (port != -1) {
337                                     Log.d(TAG, "Local proxy is bound on " + port);
338                                     sendProxyIfNeeded();
339                                 } 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                 }
349             }
350         };
351         mContext.bindService(intent, mProxyConnection,
352                 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE);
353     }
354
355     private void unbind() {
356         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;
365         mLastPort = -1;
366     }
367
368     private void sendPacBroadcast(ProxyProperties proxy) {
369         mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
370     }
371
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     }
381 }