blob: 11f34cc8619f9b3ce1be438ce62885a295cbafc5 [file] [log] [blame]
The Android Open Source Project54a942f2008-10-21 07:00:00 -07001/*
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;
38import android.telephony.TelephonyManager;
39import android.util.EventLog;
40import android.util.Log;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44
45/**
46 * {@hide}
47 */
48public class ConnectivityService extends IConnectivityManager.Stub {
49
50 private static final boolean DBG = false;
51 private static final String TAG = "ConnectivityService";
52
53 // Event log tags (must be in sync with event-log-tags)
54 private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
55
56 /**
57 * Sometimes we want to refer to the individual network state
58 * trackers separately, and sometimes we just want to treat them
59 * abstractly.
60 */
61 private NetworkStateTracker mNetTrackers[];
62 private boolean mTeardownRequested[];
63 private WifiStateTracker mWifiStateTracker;
64 private MobileDataStateTracker mMobileDataStateTracker;
65 private WifiWatchdogService mWifiWatchdogService;
66
67 private Context mContext;
68 private int mNetworkPreference;
69 private NetworkStateTracker mActiveNetwork;
70
71 private int mNumDnsEntries;
72 private static int mDnsChangeCounter;
73
74 private boolean mTestMode;
75 private static ConnectivityService sServiceInstance;
76
77 private static class ConnectivityThread extends Thread {
78 private Context mContext;
79
80 private ConnectivityThread(Context context) {
81 super("ConnectivityThread");
82 mContext = context;
83 }
84
85 @Override
86 public void run() {
87 Looper.prepare();
88 synchronized (this) {
89 sServiceInstance = new ConnectivityService(mContext);
90 notifyAll();
91 }
92 Looper.loop();
93 }
94
95 public static ConnectivityService getServiceInstance(Context context) {
96 ConnectivityThread thread = new ConnectivityThread(context);
97 thread.start();
98
99 synchronized (thread) {
100 while (sServiceInstance == null) {
101 try {
102 // Wait until sServiceInstance has been initialized.
103 thread.wait();
104 } catch (InterruptedException e) {
105 }
106 }
107 }
108
109 return sServiceInstance;
110 }
111 }
112
113 public static ConnectivityService getInstance(Context context) {
114 return ConnectivityThread.getServiceInstance(context);
115 }
116
117 private ConnectivityService(Context context) {
118 if (DBG) Log.v(TAG, "ConnectivityService starting up");
119 mContext = context;
120 mNetTrackers = new NetworkStateTracker[2];
121 mTeardownRequested = new boolean[2];
122 Handler handler = new MyHandler();
123
124 mNetworkPreference = getPersistedNetworkPreference();
125
126 /*
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 // The WifiStateTracker should appear first in the list
138 mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
139
140 mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
141 mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
142
143 mActiveNetwork = null;
144 mNumDnsEntries = 0;
145
146 mTestMode = SystemProperties.get("cm.test.mode").equals("true")
147 && SystemProperties.get("ro.build.type").equals("eng");
148
149 for (NetworkStateTracker t : mNetTrackers)
150 t.startMonitoring();
151
152 // Constructing this starts it too
153 mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
154 }
155
156 /**
157 * Sets the preferred network.
158 * @param preference the new preference
159 */
160 public synchronized void setNetworkPreference(int preference) {
161 enforceChangePermission();
162 if (ConnectivityManager.isNetworkTypeValid(preference)) {
163 int oldPreference = mNetworkPreference;
164 persistNetworkPreference(preference);
165 if (mNetworkPreference != oldPreference)
166 enforcePreference();
167 }
168 }
169
170 public int getNetworkPreference() {
171 enforceAccessPermission();
172 return mNetworkPreference;
173 }
174
175 private void persistNetworkPreference(int networkPreference) {
176 final ContentResolver cr = mContext.getContentResolver();
177 Settings.System.putInt(cr, Settings.System.NETWORK_PREFERENCE, networkPreference);
178 }
179
180 private int getPersistedNetworkPreference() {
181 final ContentResolver cr = mContext.getContentResolver();
182
183 final int networkPrefSetting = Settings.System
184 .getInt(cr, Settings.System.NETWORK_PREFERENCE, -1);
185 if (networkPrefSetting != -1) {
186 return networkPrefSetting;
187 }
188
189 return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
190 }
191
192 /**
193 * Make the state of network connectivity conform to the preference settings.
194 * In this method, we only tear down a non-preferred network. Establishing
195 * a connection to the preferred network is taken care of when we handle
196 * the disconnect event from the non-preferred network
197 * (see {@link #handleDisconnect(NetworkInfo)}).
198 */
199 private void enforcePreference() {
200 if (mActiveNetwork == null)
201 return;
202
203 for (NetworkStateTracker t : mNetTrackers) {
204 if (t == mActiveNetwork) {
205 int netType = t.getNetworkInfo().getType();
206 int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
207 ConnectivityManager.TYPE_MOBILE :
208 ConnectivityManager.TYPE_WIFI);
209
210 if (t.getNetworkInfo().getType() != mNetworkPreference) {
211 NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
212 if (otherTracker.isAvailable()) {
213 teardown(t);
214 }
215 }
216 }
217 }
218 }
219
220 private boolean teardown(NetworkStateTracker netTracker) {
221 if (netTracker.teardown()) {
222 mTeardownRequested[netTracker.getNetworkInfo().getType()] = true;
223 return true;
224 } else {
225 return false;
226 }
227 }
228
229 /**
230 * Return NetworkInfo for the active network interface. It is assumed that at most
231 * one network is active at a time. If more than one is active, it is indeterminate
232 * which will be returned.
233 * @return the info for the active network, or {@code null} if none is active
234 */
235 public NetworkInfo getActiveNetworkInfo() {
236 enforceAccessPermission();
237 for (NetworkStateTracker t : mNetTrackers) {
238 NetworkInfo info = t.getNetworkInfo();
239 if (info.isConnected()) {
240 return info;
241 }
242 }
243 return null;
244 }
245
246 public NetworkInfo getNetworkInfo(int networkType) {
247 enforceAccessPermission();
248 if (ConnectivityManager.isNetworkTypeValid(networkType)) {
249 NetworkStateTracker t = mNetTrackers[networkType];
250 if (t != null)
251 return t.getNetworkInfo();
252 }
253 return null;
254 }
255
256 public NetworkInfo[] getAllNetworkInfo() {
257 enforceAccessPermission();
258 NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
259 int i = 0;
260 for (NetworkStateTracker t : mNetTrackers) {
261 result[i++] = t.getNetworkInfo();
262 }
263 return result;
264 }
265
266 public boolean setRadios(boolean turnOn) {
267 boolean result = true;
268 enforceChangePermission();
269 for (NetworkStateTracker t : mNetTrackers) {
270 result = t.setRadio(turnOn) && result;
271 }
272 return result;
273 }
274
275 public boolean setRadio(int netType, boolean turnOn) {
276 enforceChangePermission();
277 if (!ConnectivityManager.isNetworkTypeValid(netType)) {
278 return false;
279 }
280 NetworkStateTracker tracker = mNetTrackers[netType];
281 return tracker != null && tracker.setRadio(turnOn);
282 }
283
284 public int startUsingNetworkFeature(int networkType, String feature) {
285 enforceChangePermission();
286 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
287 return -1;
288 }
289 NetworkStateTracker tracker = mNetTrackers[networkType];
290 if (tracker != null) {
291 return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
292 }
293 return -1;
294
295 }
296
297 public int stopUsingNetworkFeature(int networkType, String feature) {
298 enforceChangePermission();
299 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
300 return -1;
301 }
302 NetworkStateTracker tracker = mNetTrackers[networkType];
303 if (tracker != null) {
304 return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
305 }
306 return -1;
307 }
308
309 /**
310 * Ensure that a network route exists to deliver traffic to the specified
311 * host via the specified network interface.
312 * @param networkType the type of the network over which traffic to the specified
313 * host is to be routed
314 * @param hostAddress the IP address of the host to which the route is desired
315 * @return {@code true} on success, {@code false} on failure
316 */
317 public boolean requestRouteToHost(int networkType, int hostAddress) {
318 enforceChangePermission();
319 if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
320 return false;
321 }
322 NetworkStateTracker tracker = mNetTrackers[networkType];
323 /*
324 * If there's only one connected network, and it's the one requested,
325 * then we don't have to do anything - the requested route already
326 * exists. If it's not the requested network, then it's not possible
327 * to establish the requested route. Finally, if there is more than
328 * one connected network, then we must insert an entry in the routing
329 * table.
330 */
331 if (getNumConnectedNetworks() > 1) {
332 return tracker.requestRouteToHost(hostAddress);
333 } else {
334 return tracker.getNetworkInfo().getType() == networkType;
335 }
336 }
337
338 private int getNumConnectedNetworks() {
339 int numConnectedNets = 0;
340
341 for (NetworkStateTracker nt : mNetTrackers) {
342 if (nt.getNetworkInfo().isConnected()
343 && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
344 ++numConnectedNets;
345 }
346 }
347 return numConnectedNets;
348 }
349
350 private void enforceAccessPermission() {
351 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
352 "ConnectivityService");
353 }
354
355 private void enforceChangePermission() {
356 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
357 "ConnectivityService");
358
359 }
360
361 /**
362 * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
363 * we ignore it. If it is for the active network, we send out a broadcast.
364 * But first, we check whether it might be possible to connect to a different
365 * network.
366 * @param info the {@code NetworkInfo} for the network
367 */
368 private void handleDisconnect(NetworkInfo info) {
369
370 if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
371
372 /*
373 * If the disconnected network is not the active one, then don't report
374 * this as a loss of connectivity. What probably happened is that we're
375 * getting the disconnect for a network that we explicitly disabled
376 * in accordance with network preference policies.
377 */
378 mTeardownRequested[info.getType()] = false;
379 if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType())
380 return;
381
382 NetworkStateTracker newNet;
383 if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
384 newNet = mWifiStateTracker;
385 } else /* info().getType() == TYPE_WIFI */ {
386 newNet = mMobileDataStateTracker;
387 }
388
389 NetworkInfo switchTo = null;
390 if (newNet.isAvailable()) {
391 mActiveNetwork = newNet;
392 switchTo = newNet.getNetworkInfo();
393 switchTo.setFailover(true);
394 if (!switchTo.isConnectedOrConnecting())
395 newNet.reconnect();
396 }
397
398 boolean otherNetworkConnected = false;
399 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
400 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
401 if (info.isFailover()) {
402 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
403 info.setFailover(false);
404 }
405 if (info.getReason() != null) {
406 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
407 }
408 if (info.getExtraInfo() != null) {
409 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
410 }
411 if (switchTo != null) {
412 otherNetworkConnected = switchTo.isConnected();
413 if (DBG) {
414 if (otherNetworkConnected) {
415 Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
416 } else {
417 Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
418 }
419 }
420 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
421 } else {
422 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
423 }
424 if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
425 (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
426
427 mContext.sendStickyBroadcast(intent);
428 /*
429 * If the failover network is already connected, then immediately send out
430 * a followup broadcast indicating successful failover
431 */
432 if (switchTo != null && otherNetworkConnected)
433 sendConnectedBroadcast(switchTo);
434 }
435
436 private void sendConnectedBroadcast(NetworkInfo info) {
437 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
438 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
439 if (info.isFailover()) {
440 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
441 info.setFailover(false);
442 }
443 if (info.getReason() != null) {
444 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
445 }
446 if (info.getExtraInfo() != null) {
447 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
448 }
449 mContext.sendStickyBroadcast(intent);
450 }
451
452 private void handleConnectionFailure(NetworkInfo info) {
453 mTeardownRequested[info.getType()] = false;
454 if (getActiveNetworkInfo() == null) {
455 String reason = info.getReason();
456 String extraInfo = info.getExtraInfo();
457
458 if (DBG) {
459 String reasonText;
460 if (reason == null) {
461 reasonText = ".";
462 } else {
463 reasonText = " (" + reason + ").";
464 }
465 Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
466 }
467
468 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
469 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
470 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
471 if (reason != null) {
472 intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
473 }
474 if (extraInfo != null) {
475 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
476 }
477 if (info.isFailover()) {
478 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
479 info.setFailover(false);
480 }
481 mContext.sendStickyBroadcast(intent);
482 }
483 }
484
485 private void handleConnect(NetworkInfo info) {
486 if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
487
488 // snapshot isFailover, because sendConnectedBroadcast() resets it
489 boolean isFailover = info.isFailover();
490 NetworkStateTracker thisNet = mNetTrackers[info.getType()];
491 NetworkStateTracker deadnet = null;
492 NetworkStateTracker otherNet;
493 if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
494 otherNet = mWifiStateTracker;
495 } else /* info().getType() == TYPE_WIFI */ {
496 otherNet = mMobileDataStateTracker;
497 }
498 /*
499 * Check policy to see whether we are now connected to a network that
500 * takes precedence over the other one. If so, we need to tear down
501 * the other one.
502 */
503 NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
504 NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
505 if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
506 if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
507 deadnet = mMobileDataStateTracker;
508 else
509 deadnet = mWifiStateTracker;
510 }
511
512 boolean toredown = false;
513 mTeardownRequested[info.getType()] = false;
514 if (!mTestMode && deadnet != null) {
515 if (DBG) Log.v(TAG, "Policy requires " +
516 deadnet.getNetworkInfo().getTypeName() + " teardown");
517 toredown = teardown(deadnet);
518 if (DBG && !toredown) {
519 Log.d(TAG, "Network declined teardown request");
520 }
521 }
522
523 if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
524 mActiveNetwork = thisNet;
525 if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
526 thisNet.updateNetworkSettings();
527 sendConnectedBroadcast(info);
528 if (isFailover) {
529 otherNet.releaseWakeLock();
530 }
531 } else {
532 if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
533 info.getTypeName());
534 }
535 }
536
537 private void handleScanResultsAvailable(NetworkInfo info) {
538 int networkType = info.getType();
539 if (networkType != ConnectivityManager.TYPE_WIFI) {
540 if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
541 + " Don't know how to handle.");
542 }
543
544 mNetTrackers[networkType].interpretScanResultsAvailable();
545 }
546
547 private void handleNotificationChange(boolean visible, int id, Notification notification) {
548 NotificationManager notificationManager = (NotificationManager) mContext
549 .getSystemService(Context.NOTIFICATION_SERVICE);
550
551 if (visible) {
552 notificationManager.notify(id, notification);
553 } else {
554 notificationManager.cancel(id);
555 }
556 }
557
558 /**
559 * After any kind of change in the connectivity state of any network,
560 * make sure that anything that depends on the connectivity state of
561 * more than one network is set up correctly. We're mainly concerned
562 * with making sure that the list of DNS servers is set up according
563 * to which networks are connected, and ensuring that the right routing
564 * table entries exist.
565 */
566 private void handleConnectivityChange() {
567 /*
568 * If both mobile and wifi are enabled, add the host routes that
569 * will allow MMS traffic to pass on the mobile network. But
570 * remove the default route for the mobile network, so that there
571 * will be only one default route, to ensure that all traffic
572 * except MMS will travel via Wi-Fi.
573 */
574 int numConnectedNets = handleConfigurationChange();
575 if (numConnectedNets > 1) {
576 mMobileDataStateTracker.addPrivateRoutes();
577 mMobileDataStateTracker.removeDefaultRoute();
578 } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
579 mMobileDataStateTracker.removePrivateRoutes();
580 mMobileDataStateTracker.restoreDefaultRoute();
581 }
582 }
583
584 private int handleConfigurationChange() {
585 /*
586 * Set DNS properties. Always put Wi-Fi entries at the front of
587 * the list if it is active.
588 */
589 int index = 1;
590 String lastDns = "";
591 int numConnectedNets = 0;
592 int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
593 int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
594
595 for (int net = ConnectivityManager.TYPE_WIFI; net != stopValue; net += incrValue) {
596 NetworkStateTracker nt = mNetTrackers[net];
597 if (nt.getNetworkInfo().isConnected()
598 && !mTeardownRequested[nt.getNetworkInfo().getType()]) {
599 ++numConnectedNets;
600 String[] dnsList = nt.getNameServers();
601 for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
602 // skip duplicate entries
603 if (!dnsList[i].equals(lastDns)) {
604 SystemProperties.set("net.dns" + index++, dnsList[i]);
605 lastDns = dnsList[i];
606 }
607 }
608 }
609 }
610 // Null out any DNS properties that are no longer used
611 for (int i = index; i <= mNumDnsEntries; i++) {
612 SystemProperties.set("net.dns" + i, "");
613 }
614 mNumDnsEntries = index - 1;
615 // Notify the name resolver library of the change
616 SystemProperties.set("net.dnschange", String.valueOf(mDnsChangeCounter++));
617 return numConnectedNets;
618 }
619
620 @Override
621 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
622 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP)
623 != PackageManager.PERMISSION_GRANTED) {
624 pw.println("Permission Denial: can't dump ConnectivityService from from pid="
625 + Binder.getCallingPid()
626 + ", uid=" + Binder.getCallingUid());
627 return;
628 }
629 if (mActiveNetwork == null) {
630 pw.println("No active network");
631 } else {
632 pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
633 }
634 pw.println();
635 for (NetworkStateTracker nst : mNetTrackers) {
636 pw.println(nst.getNetworkInfo());
637 pw.println(nst);
638 pw.println();
639 }
640 }
641
642 private class MyHandler extends Handler {
643 @Override
644 public void handleMessage(Message msg) {
645 NetworkInfo info;
646 switch (msg.what) {
647 case NetworkStateTracker.EVENT_STATE_CHANGED:
648 info = (NetworkInfo) msg.obj;
649 if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
650 info.getState() + "/" + info.getDetailedState());
651
652 // Connectivity state changed:
653 // [31-11] Reserved for future use
654 // [10-9] Mobile network connection type (as defined by the TelephonyManager)
655 // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
656 // [2-0] Network type (as defined by ConnectivityManager)
657 int eventLogParam = (info.getType() & 0x7) |
658 ((info.getDetailedState().ordinal() & 0x3f) << 3) |
659 (TelephonyManager.getDefault().getNetworkType() << 9);
660 EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
661
662 if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
663 handleConnectionFailure(info);
664 } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
665 handleDisconnect(info);
666 } else if (info.getState() == NetworkInfo.State.SUSPENDED) {
667 // TODO: need to think this over.
668 // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
669 // only difference being we are broadcasting an intent with NetworkInfo
670 // that's suspended. This allows the applications an opportunity to
671 // handle DISCONNECTED and SUSPENDED differently, or not.
672 handleDisconnect(info);
673 } else if (info.getState() == NetworkInfo.State.CONNECTED) {
674 handleConnect(info);
675 }
676 handleConnectivityChange();
677 break;
678
679 case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
680 info = (NetworkInfo) msg.obj;
681 handleScanResultsAvailable(info);
682 break;
683
684 case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
685 handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
686
687 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
688 handleConfigurationChange();
689 break;
690 }
691 }
692 }
693}