blob: 62b839dc1861cf8dc8d98d13ec54404aa9210f0e [file] [log] [blame]
The Android Open Source Project28527d22009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
17package com.android.server;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.net.ConnectivityManager;
26import android.net.IConnectivityManager;
27import android.net.MobileDataStateTracker;
28import android.net.NetworkInfo;
29import android.net.NetworkStateTracker;
30import android.net.wifi.WifiStateTracker;
31import android.os.Binder;
32import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
35import android.os.ServiceManager;
36import android.os.SystemProperties;
37import android.provider.Settings;
The Android Open Source Project28527d22009-03-03 19:31:44 -080038import android.util.EventLog;
39import android.util.Log;
40
41import java.io.FileDescriptor;
42import java.io.PrintWriter;
43
44/**
45 * @hide
46 */
47public class ConnectivityService extends IConnectivityManager.Stub {
48
49 private static final boolean DBG = false;
50 private static final String TAG = "ConnectivityService";
51
52 // Event log tags (must be in sync with event-log-tags)
53 private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
54
55 /**
56 * Sometimes we want to refer to the individual network state
57 * trackers separately, and sometimes we just want to treat them
58 * abstractly.
59 */
60 private NetworkStateTracker mNetTrackers[];
61 private WifiStateTracker mWifiStateTracker;
62 private MobileDataStateTracker mMobileDataStateTracker;
63 private WifiWatchdogService mWifiWatchdogService;
64
65 private Context mContext;
66 private int mNetworkPreference;
67 private NetworkStateTracker mActiveNetwork;
68
69 private int mNumDnsEntries;
70 private static int sDnsChangeCounter;
71
72 private boolean mTestMode;
73 private static ConnectivityService sServiceInstance;
74
75 private static class ConnectivityThread extends Thread {
76 private Context mContext;
Robert Greenwalt0659da32009-07-16 17:21:39 -070077
The Android Open Source Project28527d22009-03-03 19:31:44 -080078 private ConnectivityThread(Context context) {
79 super("ConnectivityThread");
80 mContext = context;
81 }
82
83 @Override
84 public void run() {
85 Looper.prepare();
86 synchronized (this) {
87 sServiceInstance = new ConnectivityService(mContext);
88 notifyAll();
89 }
90 Looper.loop();
91 }
Robert Greenwalt0659da32009-07-16 17:21:39 -070092
The Android Open Source Project28527d22009-03-03 19:31:44 -080093 public static ConnectivityService getServiceInstance(Context context) {
94 ConnectivityThread thread = new ConnectivityThread(context);
95 thread.start();
Robert Greenwalt0659da32009-07-16 17:21:39 -070096
The Android Open Source Project28527d22009-03-03 19:31:44 -080097 synchronized (thread) {
98 while (sServiceInstance == null) {
99 try {
100 // Wait until sServiceInstance has been initialized.
101 thread.wait();
102 } catch (InterruptedException ignore) {
103 Log.e(TAG,
Robert Greenwalt0659da32009-07-16 17:21:39 -0700104 "Unexpected InterruptedException while waiting"+
105 " for ConnectivityService thread");
The Android Open Source Project28527d22009-03-03 19:31:44 -0800106 }
107 }
108 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700109
The Android Open Source Project28527d22009-03-03 19:31:44 -0800110 return sServiceInstance;
111 }
112 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700113
The Android Open Source Project28527d22009-03-03 19:31:44 -0800114 public static ConnectivityService getInstance(Context context) {
115 return ConnectivityThread.getServiceInstance(context);
116 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700117
The Android Open Source Project28527d22009-03-03 19:31:44 -0800118 private ConnectivityService(Context context) {
119 if (DBG) Log.v(TAG, "ConnectivityService starting up");
120 mContext = context;
121 mNetTrackers = new NetworkStateTracker[2];
122 Handler handler = new MyHandler();
Robert Greenwalt0659da32009-07-16 17:21:39 -0700123
The Android Open Source Project28527d22009-03-03 19:31:44 -0800124 mNetworkPreference = getPersistedNetworkPreference();
Robert Greenwalt0659da32009-07-16 17:21:39 -0700125
The Android Open Source Project28527d22009-03-03 19:31:44 -0800126 /*
127 * Create the network state trackers for Wi-Fi and mobile
128 * data. Maybe this could be done with a factory class,
129 * but it's not clear that it's worth it, given that
130 * the number of different network types is not going
131 * to change very often.
132 */
133 if (DBG) Log.v(TAG, "Starting Wifi Service.");
134 mWifiStateTracker = new WifiStateTracker(context, handler);
135 WifiService wifiService = new WifiService(context, mWifiStateTracker);
136 ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
137 mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
138
139 mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
140 mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
Robert Greenwalt0659da32009-07-16 17:21:39 -0700141
The Android Open Source Project28527d22009-03-03 19:31:44 -0800142 mActiveNetwork = null;
143 mNumDnsEntries = 0;
144
145 mTestMode = SystemProperties.get("cm.test.mode").equals("true")
146 && SystemProperties.get("ro.build.type").equals("eng");
147
148 for (NetworkStateTracker t : mNetTrackers)
149 t.startMonitoring();
150
151 // Constructing this starts it too
Robert Greenwalt0659da32009-07-16 17:21:39 -0700152 mWifiWatchdogService = new WifiWatchdogService(context,
153 mWifiStateTracker);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800154 }
155
156 /**
Robert Greenwalt0659da32009-07-16 17:21:39 -0700157 * Sets the preferred network.
The Android Open Source Project28527d22009-03-03 19:31:44 -0800158 * @param preference the new preference
159 */
160 public synchronized void setNetworkPreference(int preference) {
161 enforceChangePermission();
162 if (ConnectivityManager.isNetworkTypeValid(preference)) {
163 if (mNetworkPreference != preference) {
164 persistNetworkPreference(preference);
165 mNetworkPreference = preference;
166 enforcePreference();
167 }
168 }
169 }
170
171 public int getNetworkPreference() {
172 enforceAccessPermission();
173 return mNetworkPreference;
174 }
175
176 private void persistNetworkPreference(int networkPreference) {
177 final ContentResolver cr = mContext.getContentResolver();
Robert Greenwalt0659da32009-07-16 17:21:39 -0700178 Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
179 networkPreference);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800180 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700181
The Android Open Source Project28527d22009-03-03 19:31:44 -0800182 private int getPersistedNetworkPreference() {
183 final ContentResolver cr = mContext.getContentResolver();
184
185 final int networkPrefSetting = Settings.Secure
186 .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1);
187 if (networkPrefSetting != -1) {
188 return networkPrefSetting;
189 }
190
191 return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
192 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700193
The Android Open Source Project28527d22009-03-03 19:31:44 -0800194 /**
Robert Greenwalt0659da32009-07-16 17:21:39 -0700195 * Make the state of network connectivity conform to the preference settings
The Android Open Source Project28527d22009-03-03 19:31:44 -0800196 * In this method, we only tear down a non-preferred network. Establishing
197 * a connection to the preferred network is taken care of when we handle
198 * the disconnect event from the non-preferred network
199 * (see {@link #handleDisconnect(NetworkInfo)}).
200 */
201 private void enforcePreference() {
202 if (mActiveNetwork == null)
203 return;
204
205 for (NetworkStateTracker t : mNetTrackers) {
206 if (t == mActiveNetwork) {
207 int netType = t.getNetworkInfo().getType();
208 int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
209 ConnectivityManager.TYPE_MOBILE :
210 ConnectivityManager.TYPE_WIFI);
211
212 if (t.getNetworkInfo().getType() != mNetworkPreference) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700213 NetworkStateTracker otherTracker =
214 mNetTrackers[otherNetType];
The Android Open Source Project28527d22009-03-03 19:31:44 -0800215 if (otherTracker.isAvailable()) {
216 teardown(t);
217 }
218 }
219 }
220 }
221 }
222
223 private boolean teardown(NetworkStateTracker netTracker) {
224 if (netTracker.teardown()) {
225 netTracker.setTeardownRequested(true);
226 return true;
227 } else {
228 return false;
229 }
230 }
231
232 /**
233 * Return NetworkInfo for the active (i.e., connected) network interface.
234 * It is assumed that at most one network is active at a time. If more
235 * than one is active, it is indeterminate which will be returned.
Robert Greenwalt0659da32009-07-16 17:21:39 -0700236 * @return the info for the active network, or {@code null} if none is
237 * active
The Android Open Source Project28527d22009-03-03 19:31:44 -0800238 */
239 public NetworkInfo getActiveNetworkInfo() {
240 enforceAccessPermission();
241 for (NetworkStateTracker t : mNetTrackers) {
242 NetworkInfo info = t.getNetworkInfo();
243 if (info.isConnected()) {
244 return info;
245 }
246 }
247 return null;
248 }
249
250 public NetworkInfo getNetworkInfo(int networkType) {
251 enforceAccessPermission();
252 if (ConnectivityManager.isNetworkTypeValid(networkType)) {
253 NetworkStateTracker t = mNetTrackers[networkType];
254 if (t != null)
255 return t.getNetworkInfo();
256 }
257 return null;
258 }
259
260 public NetworkInfo[] getAllNetworkInfo() {
261 enforceAccessPermission();
262 NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
263 int i = 0;
264 for (NetworkStateTracker t : mNetTrackers) {
265 result[i++] = t.getNetworkInfo();
266 }
267 return result;
268 }
269
270 public boolean setRadios(boolean turnOn) {
271 boolean result = true;
272 enforceChangePermission();
273 for (NetworkStateTracker t : mNetTrackers) {
274 result = t.setRadio(turnOn) && result;
275 }
276 return result;
277 }
278
279 public boolean setRadio(int netType, boolean turnOn) {
280 enforceChangePermission();
281 if (!ConnectivityManager.isNetworkTypeValid(netType)) {
282 return false;
283 }
284 NetworkStateTracker tracker = mNetTrackers[netType];
285 return tracker != null && tracker.setRadio(turnOn);
286 }
287
288 public int startUsingNetworkFeature(int networkType, String feature) {
289 enforceChangePermission();
290 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
291 return -1;
292 }
293 NetworkStateTracker tracker = mNetTrackers[networkType];
294 if (tracker != null) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700295 return tracker.startUsingNetworkFeature(feature, getCallingPid(),
296 getCallingUid());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800297 }
298 return -1;
299 }
300
301 public int stopUsingNetworkFeature(int networkType, String feature) {
302 enforceChangePermission();
303 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
304 return -1;
305 }
306 NetworkStateTracker tracker = mNetTrackers[networkType];
307 if (tracker != null) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700308 return tracker.stopUsingNetworkFeature(feature, getCallingPid(),
309 getCallingUid());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800310 }
311 return -1;
312 }
313
314 /**
315 * Ensure that a network route exists to deliver traffic to the specified
316 * host via the specified network interface.
Robert Greenwalt0659da32009-07-16 17:21:39 -0700317 * @param networkType the type of the network over which traffic to the
318 * specified host is to be routed
319 * @param hostAddress the IP address of the host to which the route is
320 * desired
The Android Open Source Project28527d22009-03-03 19:31:44 -0800321 * @return {@code true} on success, {@code false} on failure
322 */
323 public boolean requestRouteToHost(int networkType, int hostAddress) {
324 enforceChangePermission();
325 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
326 return false;
327 }
328 NetworkStateTracker tracker = mNetTrackers[networkType];
329 /*
330 * If there's only one connected network, and it's the one requested,
331 * then we don't have to do anything - the requested route already
332 * exists. If it's not the requested network, then it's not possible
333 * to establish the requested route. Finally, if there is more than
334 * one connected network, then we must insert an entry in the routing
335 * table.
336 */
337 if (getNumConnectedNetworks() > 1) {
338 return tracker.requestRouteToHost(hostAddress);
339 } else {
340 return tracker.getNetworkInfo().getType() == networkType;
341 }
342 }
343
344 /**
345 * @see ConnectivityManager#getBackgroundDataSetting()
346 */
347 public boolean getBackgroundDataSetting() {
348 return Settings.Secure.getInt(mContext.getContentResolver(),
349 Settings.Secure.BACKGROUND_DATA, 1) == 1;
350 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700351
The Android Open Source Project28527d22009-03-03 19:31:44 -0800352 /**
353 * @see ConnectivityManager#setBackgroundDataSetting(boolean)
354 */
355 public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
356 mContext.enforceCallingOrSelfPermission(
357 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
358 "ConnectivityService");
Robert Greenwalt0659da32009-07-16 17:21:39 -0700359
The Android Open Source Project28527d22009-03-03 19:31:44 -0800360 if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
361
362 Settings.Secure.putInt(mContext.getContentResolver(),
Robert Greenwalt0659da32009-07-16 17:21:39 -0700363 Settings.Secure.BACKGROUND_DATA,
364 allowBackgroundDataUsage ? 1 : 0);
365
The Android Open Source Project28527d22009-03-03 19:31:44 -0800366 Intent broadcast = new Intent(
367 ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
368 mContext.sendBroadcast(broadcast);
Robert Greenwalt0659da32009-07-16 17:21:39 -0700369 }
370
The Android Open Source Project28527d22009-03-03 19:31:44 -0800371 private int getNumConnectedNetworks() {
372 int numConnectedNets = 0;
373
374 for (NetworkStateTracker nt : mNetTrackers) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700375 if (nt.getNetworkInfo().isConnected() &&
376 !nt.isTeardownRequested()) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800377 ++numConnectedNets;
378 }
379 }
380 return numConnectedNets;
381 }
382
383 private void enforceAccessPermission() {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700384 mContext.enforceCallingOrSelfPermission(
385 android.Manifest.permission.ACCESS_NETWORK_STATE,
386 "ConnectivityService");
The Android Open Source Project28527d22009-03-03 19:31:44 -0800387 }
388
389 private void enforceChangePermission() {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700390 mContext.enforceCallingOrSelfPermission(
391 android.Manifest.permission.CHANGE_NETWORK_STATE,
392 "ConnectivityService");
The Android Open Source Project28527d22009-03-03 19:31:44 -0800393 }
394
395 /**
Robert Greenwalt0659da32009-07-16 17:21:39 -0700396 * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
397 * network, we ignore it. If it is for the active network, we send out a
398 * broadcast. But first, we check whether it might be possible to connect
399 * to a different network.
The Android Open Source Project28527d22009-03-03 19:31:44 -0800400 * @param info the {@code NetworkInfo} for the network
401 */
402 private void handleDisconnect(NetworkInfo info) {
403
404 if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
405
406 mNetTrackers[info.getType()].setTeardownRequested(false);
407 /*
408 * If the disconnected network is not the active one, then don't report
409 * this as a loss of connectivity. What probably happened is that we're
410 * getting the disconnect for a network that we explicitly disabled
411 * in accordance with network preference policies.
412 */
Robert Greenwalt0659da32009-07-16 17:21:39 -0700413 if (mActiveNetwork == null ||
414 info.getType() != mActiveNetwork.getNetworkInfo().getType())
The Android Open Source Project28527d22009-03-03 19:31:44 -0800415 return;
416
417 NetworkStateTracker newNet;
418 if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
419 newNet = mWifiStateTracker;
420 } else /* info().getType() == TYPE_WIFI */ {
421 newNet = mMobileDataStateTracker;
422 }
423
424 /**
425 * See if the other network is available to fail over to.
426 * If is not available, we enable it anyway, so that it
427 * will be able to connect when it does become available,
428 * but we report a total loss of connectivity rather than
429 * report that we are attempting to fail over.
430 */
431 NetworkInfo switchTo = null;
432 if (newNet.isAvailable()) {
433 mActiveNetwork = newNet;
434 switchTo = newNet.getNetworkInfo();
435 switchTo.setFailover(true);
436 if (!switchTo.isConnectedOrConnecting()) {
437 newNet.reconnect();
438 }
439 } else {
440 newNet.reconnect();
441 }
442
443 boolean otherNetworkConnected = false;
444 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
445 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
446 if (info.isFailover()) {
447 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
448 info.setFailover(false);
449 }
450 if (info.getReason() != null) {
451 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
452 }
453 if (info.getExtraInfo() != null) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700454 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
455 info.getExtraInfo());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800456 }
457 if (switchTo != null) {
458 otherNetworkConnected = switchTo.isConnected();
459 if (DBG) {
460 if (otherNetworkConnected) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700461 Log.v(TAG, "Switching to already connected " +
462 switchTo.getTypeName());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800463 } else {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700464 Log.v(TAG, "Attempting to switch to " +
465 switchTo.getTypeName());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800466 }
467 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700468 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
469 switchTo);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800470 } else {
471 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
472 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700473 if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " +
474 info.getTypeName() +
The Android Open Source Project28527d22009-03-03 19:31:44 -0800475 (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
476
477 mContext.sendStickyBroadcast(intent);
478 /*
Robert Greenwalt0659da32009-07-16 17:21:39 -0700479 * If the failover network is already connected, then immediately send
480 * out a followup broadcast indicating successful failover
The Android Open Source Project28527d22009-03-03 19:31:44 -0800481 */
482 if (switchTo != null && otherNetworkConnected)
483 sendConnectedBroadcast(switchTo);
484 }
485
486 private void sendConnectedBroadcast(NetworkInfo info) {
487 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
488 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
489 if (info.isFailover()) {
490 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
491 info.setFailover(false);
492 }
493 if (info.getReason() != null) {
494 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
495 }
496 if (info.getExtraInfo() != null) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700497 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
498 info.getExtraInfo());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800499 }
500 mContext.sendStickyBroadcast(intent);
501 }
502
503 /**
504 * Called when an attempt to fail over to another network has failed.
505 * @param info the {@link NetworkInfo} for the failed network
506 */
507 private void handleConnectionFailure(NetworkInfo info) {
508 mNetTrackers[info.getType()].setTeardownRequested(false);
509 if (getActiveNetworkInfo() == null) {
510 String reason = info.getReason();
511 String extraInfo = info.getExtraInfo();
512
513 if (DBG) {
514 String reasonText;
515 if (reason == null) {
516 reasonText = ".";
517 } else {
518 reasonText = " (" + reason + ").";
519 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700520 Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
521 " failed" + reasonText);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800522 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700523
The Android Open Source Project28527d22009-03-03 19:31:44 -0800524 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
525 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
526 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
527 if (reason != null) {
528 intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
529 }
530 if (extraInfo != null) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700531 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
532 extraInfo);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800533 }
534 if (info.isFailover()) {
535 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
536 info.setFailover(false);
537 }
538 mContext.sendStickyBroadcast(intent);
539 }
540 }
541
542 private void handleConnect(NetworkInfo info) {
543 if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
544
545 // snapshot isFailover, because sendConnectedBroadcast() resets it
546 boolean isFailover = info.isFailover();
547 NetworkStateTracker thisNet = mNetTrackers[info.getType()];
548 NetworkStateTracker deadnet = null;
549 NetworkStateTracker otherNet;
550 if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
551 otherNet = mWifiStateTracker;
552 } else /* info().getType() == TYPE_WIFI */ {
553 otherNet = mMobileDataStateTracker;
554 }
555 /*
556 * Check policy to see whether we are connected to a non-preferred
557 * network that now needs to be torn down.
558 */
559 NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
560 NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
561 if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
562 if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
563 deadnet = mMobileDataStateTracker;
564 else
565 deadnet = mWifiStateTracker;
566 }
567
568 boolean toredown = false;
569 thisNet.setTeardownRequested(false);
570 if (!mTestMode && deadnet != null) {
571 if (DBG) Log.v(TAG, "Policy requires " +
572 deadnet.getNetworkInfo().getTypeName() + " teardown");
573 toredown = teardown(deadnet);
574 if (DBG && !toredown) {
575 Log.d(TAG, "Network declined teardown request");
576 }
577 }
578
579 /*
580 * Note that if toredown is true, deadnet cannot be null, so there is
581 * no danger of a null pointer exception here..
582 */
583 if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
584 mActiveNetwork = thisNet;
Robert Greenwalt0659da32009-07-16 17:21:39 -0700585 if (DBG) Log.v(TAG, "Sending CONNECT bcast for " +
586 info.getTypeName());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800587 thisNet.updateNetworkSettings();
588 sendConnectedBroadcast(info);
589 if (isFailover) {
590 otherNet.releaseWakeLock();
591 }
592 } else {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700593 if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn " +
594 "down network " + info.getTypeName());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800595 }
596 }
597
598 private void handleScanResultsAvailable(NetworkInfo info) {
599 int networkType = info.getType();
600 if (networkType != ConnectivityManager.TYPE_WIFI) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700601 if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
602 info.getTypeName() + " network. Don't know how to handle.");
The Android Open Source Project28527d22009-03-03 19:31:44 -0800603 }
Robert Greenwalt0659da32009-07-16 17:21:39 -0700604
The Android Open Source Project28527d22009-03-03 19:31:44 -0800605 mNetTrackers[networkType].interpretScanResultsAvailable();
606 }
607
Robert Greenwalt0659da32009-07-16 17:21:39 -0700608 private void handleNotificationChange(boolean visible, int id,
609 Notification notification) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800610 NotificationManager notificationManager = (NotificationManager) mContext
611 .getSystemService(Context.NOTIFICATION_SERVICE);
Robert Greenwalt0659da32009-07-16 17:21:39 -0700612
The Android Open Source Project28527d22009-03-03 19:31:44 -0800613 if (visible) {
614 notificationManager.notify(id, notification);
615 } else {
616 notificationManager.cancel(id);
617 }
618 }
619
620 /**
621 * After any kind of change in the connectivity state of any network,
622 * make sure that anything that depends on the connectivity state of
623 * more than one network is set up correctly. We're mainly concerned
624 * with making sure that the list of DNS servers is set up according
625 * to which networks are connected, and ensuring that the right routing
626 * table entries exist.
627 */
628 private void handleConnectivityChange() {
629 /*
630 * If both mobile and wifi are enabled, add the host routes that
631 * will allow MMS traffic to pass on the mobile network. But
632 * remove the default route for the mobile network, so that there
633 * will be only one default route, to ensure that all traffic
634 * except MMS will travel via Wi-Fi.
635 */
636 int numConnectedNets = handleConfigurationChange();
637 if (numConnectedNets > 1) {
638 mMobileDataStateTracker.addPrivateRoutes();
639 mMobileDataStateTracker.removeDefaultRoute();
640 } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
641 mMobileDataStateTracker.removePrivateRoutes();
642 mMobileDataStateTracker.restoreDefaultRoute();
643 }
644 }
645
646 private int handleConfigurationChange() {
647 /*
648 * Set DNS properties. Always put Wi-Fi entries at the front of
649 * the list if it is active.
650 */
651 int index = 1;
652 String lastDns = "";
653 int numConnectedNets = 0;
Robert Greenwalt0659da32009-07-16 17:21:39 -0700654 int incrValue = ConnectivityManager.TYPE_MOBILE -
655 ConnectivityManager.TYPE_WIFI;
The Android Open Source Project28527d22009-03-03 19:31:44 -0800656 int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
657
Robert Greenwalt0659da32009-07-16 17:21:39 -0700658 for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue;
659 netType += incrValue) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800660 NetworkStateTracker nt = mNetTrackers[netType];
Robert Greenwalt0659da32009-07-16 17:21:39 -0700661 if (nt.getNetworkInfo().isConnected() &&
662 !nt.isTeardownRequested()) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800663 ++numConnectedNets;
664 String[] dnsList = nt.getNameServers();
665 for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
666 // skip duplicate entries
667 if (!dnsList[i].equals(lastDns)) {
668 SystemProperties.set("net.dns" + index++, dnsList[i]);
669 lastDns = dnsList[i];
670 }
671 }
672 }
673 }
674 // Null out any DNS properties that are no longer used
675 for (int i = index; i <= mNumDnsEntries; i++) {
676 SystemProperties.set("net.dns" + i, "");
677 }
678 mNumDnsEntries = index - 1;
679 // Notify the name resolver library of the change
Robert Greenwalt0659da32009-07-16 17:21:39 -0700680 SystemProperties.set("net.dnschange",
681 String.valueOf(sDnsChangeCounter++));
The Android Open Source Project28527d22009-03-03 19:31:44 -0800682 return numConnectedNets;
683 }
684
685 @Override
686 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700687 if (mContext.checkCallingOrSelfPermission(
688 android.Manifest.permission.DUMP)
The Android Open Source Project28527d22009-03-03 19:31:44 -0800689 != PackageManager.PERMISSION_GRANTED) {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700690 pw.println("Permission Denial: can't dump ConnectivityService " +
691 "from from pid=" + Binder.getCallingPid() + ", uid=" +
692 Binder.getCallingUid());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800693 return;
694 }
695 if (mActiveNetwork == null) {
696 pw.println("No active network");
697 } else {
Robert Greenwalt0659da32009-07-16 17:21:39 -0700698 pw.println("Active network: " +
699 mActiveNetwork.getNetworkInfo().getTypeName());
The Android Open Source Project28527d22009-03-03 19:31:44 -0800700 }
701 pw.println();
702 for (NetworkStateTracker nst : mNetTrackers) {
703 pw.println(nst.getNetworkInfo());
704 pw.println(nst);
705 pw.println();
706 }
707 }
708
709 private class MyHandler extends Handler {
710 @Override
711 public void handleMessage(Message msg) {
712 NetworkInfo info;
713 switch (msg.what) {
714 case NetworkStateTracker.EVENT_STATE_CHANGED:
715 info = (NetworkInfo) msg.obj;
Robert Greenwalt0659da32009-07-16 17:21:39 -0700716 if (DBG) Log.v(TAG, "ConnectivityChange for " +
717 info.getTypeName() + ": " +
The Android Open Source Project28527d22009-03-03 19:31:44 -0800718 info.getState() + "/" + info.getDetailedState());
719
720 // Connectivity state changed:
721 // [31-13] Reserved for future use
Robert Greenwalt0659da32009-07-16 17:21:39 -0700722 // [12-9] Network subtype (for mobile network, as defined
723 // by TelephonyManager)
724 // [8-3] Detailed state ordinal (as defined by
725 // NetworkInfo.DetailedState)
The Android Open Source Project28527d22009-03-03 19:31:44 -0800726 // [2-0] Network type (as defined by ConnectivityManager)
727 int eventLogParam = (info.getType() & 0x7) |
728 ((info.getDetailedState().ordinal() & 0x3f) << 3) |
729 (info.getSubtype() << 9);
Robert Greenwalt0659da32009-07-16 17:21:39 -0700730 EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
731 eventLogParam);
732
733 if (info.getDetailedState() ==
734 NetworkInfo.DetailedState.FAILED) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800735 handleConnectionFailure(info);
Robert Greenwalt0659da32009-07-16 17:21:39 -0700736 } else if (info.getState() ==
737 NetworkInfo.State.DISCONNECTED) {
The Android Open Source Project28527d22009-03-03 19:31:44 -0800738 handleDisconnect(info);
739 } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
740 // TODO: need to think this over.
Robert Greenwalt0659da32009-07-16 17:21:39 -0700741 // the logic here is, handle SUSPENDED the same as
742 // DISCONNECTED. The only difference being we are
743 // broadcasting an intent with NetworkInfo that's
744 // suspended. This allows the applications an
745 // opportunity to handle DISCONNECTED and SUSPENDED
746 // differently, or not.
The Android Open Source Project28527d22009-03-03 19:31:44 -0800747 handleDisconnect(info);
748 } else if (info.getState() == NetworkInfo.State.CONNECTED) {
749 handleConnect(info);
750 }
751 handleConnectivityChange();
752 break;
753
754 case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
755 info = (NetworkInfo) msg.obj;
756 handleScanResultsAvailable(info);
757 break;
Robert Greenwalt0659da32009-07-16 17:21:39 -0700758
The Android Open Source Project28527d22009-03-03 19:31:44 -0800759 case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
Robert Greenwalt0659da32009-07-16 17:21:39 -0700760 handleNotificationChange(msg.arg1 == 1, msg.arg2,
761 (Notification) msg.obj);
The Android Open Source Project28527d22009-03-03 19:31:44 -0800762
763 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
764 handleConfigurationChange();
765 break;
766
767 case NetworkStateTracker.EVENT_ROAMING_CHANGED:
768 // fill me in
769 break;
770
771 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
772 // fill me in
773 break;
774 }
775 }
776 }
777}