The previous implementation used a database for storing all of its state, which could cause a significant amount of IO activity as its tables were updated through the stages of a sync. This new implementation replaces that in-memory data structures, with hand-written code for writing them to persistent storage. There are now 4 files associated with this class, holding various pieces of its state that should be consistent. These are everything from a main XML file of account information that must always be retained, to a binary file of per-day statistics that can be thrown away at any time. Writes of these files as scheduled at various times based on their importance of the frequency at which they change. Because the database no longer exists, there needs to be a new explicit interface for interacting with the sync manager database. This is provided by new APIs on IContentService, with a hidden method on ContentResolver to retrieve the IContentService so that various system entities can use it. Other changes in other projects are required to update to the new API. The goal here is to have as little an impact on the code and functionality outside of SyncStorageEngine, though due to the necessary change in API it is still somewhat extensive.
741 lines
29 KiB
Java
741 lines
29 KiB
Java
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server;
|
|
|
|
import android.app.Notification;
|
|
import android.app.NotificationManager;
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.IConnectivityManager;
|
|
import android.net.MobileDataStateTracker;
|
|
import android.net.NetworkInfo;
|
|
import android.net.NetworkStateTracker;
|
|
import android.net.wifi.WifiStateTracker;
|
|
import android.os.Binder;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.ServiceManager;
|
|
import android.os.SystemProperties;
|
|
import android.provider.Settings;
|
|
import android.util.EventLog;
|
|
import android.util.Log;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public class ConnectivityService extends IConnectivityManager.Stub {
|
|
|
|
private static final boolean DBG = false;
|
|
private static final String TAG = "ConnectivityService";
|
|
|
|
// Event log tags (must be in sync with event-log-tags)
|
|
private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
|
|
|
|
/**
|
|
* Sometimes we want to refer to the individual network state
|
|
* trackers separately, and sometimes we just want to treat them
|
|
* abstractly.
|
|
*/
|
|
private NetworkStateTracker mNetTrackers[];
|
|
private WifiStateTracker mWifiStateTracker;
|
|
private MobileDataStateTracker mMobileDataStateTracker;
|
|
private WifiWatchdogService mWifiWatchdogService;
|
|
|
|
private Context mContext;
|
|
private int mNetworkPreference;
|
|
private NetworkStateTracker mActiveNetwork;
|
|
|
|
private int mNumDnsEntries;
|
|
private static int sDnsChangeCounter;
|
|
|
|
private boolean mTestMode;
|
|
private static ConnectivityService sServiceInstance;
|
|
|
|
private static class ConnectivityThread extends Thread {
|
|
private Context mContext;
|
|
|
|
private ConnectivityThread(Context context) {
|
|
super("ConnectivityThread");
|
|
mContext = context;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
Looper.prepare();
|
|
synchronized (this) {
|
|
sServiceInstance = new ConnectivityService(mContext);
|
|
notifyAll();
|
|
}
|
|
Looper.loop();
|
|
}
|
|
|
|
public static ConnectivityService getServiceInstance(Context context) {
|
|
ConnectivityThread thread = new ConnectivityThread(context);
|
|
thread.start();
|
|
|
|
synchronized (thread) {
|
|
while (sServiceInstance == null) {
|
|
try {
|
|
// Wait until sServiceInstance has been initialized.
|
|
thread.wait();
|
|
} catch (InterruptedException ignore) {
|
|
Log.e(TAG,
|
|
"Unexpected InterruptedException while waiting for ConnectivityService thread");
|
|
}
|
|
}
|
|
}
|
|
|
|
return sServiceInstance;
|
|
}
|
|
}
|
|
|
|
public static ConnectivityService getInstance(Context context) {
|
|
return ConnectivityThread.getServiceInstance(context);
|
|
}
|
|
|
|
private ConnectivityService(Context context) {
|
|
if (DBG) Log.v(TAG, "ConnectivityService starting up");
|
|
mContext = context;
|
|
mNetTrackers = new NetworkStateTracker[2];
|
|
Handler handler = new MyHandler();
|
|
|
|
mNetworkPreference = getPersistedNetworkPreference();
|
|
|
|
/*
|
|
* Create the network state trackers for Wi-Fi and mobile
|
|
* data. Maybe this could be done with a factory class,
|
|
* but it's not clear that it's worth it, given that
|
|
* the number of different network types is not going
|
|
* to change very often.
|
|
*/
|
|
if (DBG) Log.v(TAG, "Starting Wifi Service.");
|
|
mWifiStateTracker = new WifiStateTracker(context, handler);
|
|
WifiService wifiService = new WifiService(context, mWifiStateTracker);
|
|
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
|
|
mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
|
|
|
|
mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
|
|
mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
|
|
|
|
mActiveNetwork = null;
|
|
mNumDnsEntries = 0;
|
|
|
|
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
|
|
&& SystemProperties.get("ro.build.type").equals("eng");
|
|
|
|
for (NetworkStateTracker t : mNetTrackers)
|
|
t.startMonitoring();
|
|
|
|
// Constructing this starts it too
|
|
mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
|
|
}
|
|
|
|
/**
|
|
* Sets the preferred network.
|
|
* @param preference the new preference
|
|
*/
|
|
public synchronized void setNetworkPreference(int preference) {
|
|
enforceChangePermission();
|
|
if (ConnectivityManager.isNetworkTypeValid(preference)) {
|
|
if (mNetworkPreference != preference) {
|
|
persistNetworkPreference(preference);
|
|
mNetworkPreference = preference;
|
|
enforcePreference();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getNetworkPreference() {
|
|
enforceAccessPermission();
|
|
return mNetworkPreference;
|
|
}
|
|
|
|
private void persistNetworkPreference(int networkPreference) {
|
|
final ContentResolver cr = mContext.getContentResolver();
|
|
Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
|
|
}
|
|
|
|
private int getPersistedNetworkPreference() {
|
|
final ContentResolver cr = mContext.getContentResolver();
|
|
|
|
final int networkPrefSetting = Settings.Secure
|
|
.getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1);
|
|
if (networkPrefSetting != -1) {
|
|
return networkPrefSetting;
|
|
}
|
|
|
|
return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
|
|
}
|
|
|
|
/**
|
|
* Make the state of network connectivity conform to the preference settings.
|
|
* In this method, we only tear down a non-preferred network. Establishing
|
|
* a connection to the preferred network is taken care of when we handle
|
|
* the disconnect event from the non-preferred network
|
|
* (see {@link #handleDisconnect(NetworkInfo)}).
|
|
*/
|
|
private void enforcePreference() {
|
|
if (mActiveNetwork == null)
|
|
return;
|
|
|
|
for (NetworkStateTracker t : mNetTrackers) {
|
|
if (t == mActiveNetwork) {
|
|
int netType = t.getNetworkInfo().getType();
|
|
int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
|
|
ConnectivityManager.TYPE_MOBILE :
|
|
ConnectivityManager.TYPE_WIFI);
|
|
|
|
if (t.getNetworkInfo().getType() != mNetworkPreference) {
|
|
NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
|
|
if (otherTracker.isAvailable()) {
|
|
teardown(t);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean teardown(NetworkStateTracker netTracker) {
|
|
if (netTracker.teardown()) {
|
|
netTracker.setTeardownRequested(true);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return NetworkInfo for the active (i.e., connected) network interface.
|
|
* It is assumed that at most one network is active at a time. If more
|
|
* than one is active, it is indeterminate which will be returned.
|
|
* @return the info for the active network, or {@code null} if none is active
|
|
*/
|
|
public NetworkInfo getActiveNetworkInfo() {
|
|
enforceAccessPermission();
|
|
for (NetworkStateTracker t : mNetTrackers) {
|
|
NetworkInfo info = t.getNetworkInfo();
|
|
if (info.isConnected()) {
|
|
return info;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public NetworkInfo getNetworkInfo(int networkType) {
|
|
enforceAccessPermission();
|
|
if (ConnectivityManager.isNetworkTypeValid(networkType)) {
|
|
NetworkStateTracker t = mNetTrackers[networkType];
|
|
if (t != null)
|
|
return t.getNetworkInfo();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public NetworkInfo[] getAllNetworkInfo() {
|
|
enforceAccessPermission();
|
|
NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
|
|
int i = 0;
|
|
for (NetworkStateTracker t : mNetTrackers) {
|
|
result[i++] = t.getNetworkInfo();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public boolean setRadios(boolean turnOn) {
|
|
boolean result = true;
|
|
enforceChangePermission();
|
|
for (NetworkStateTracker t : mNetTrackers) {
|
|
result = t.setRadio(turnOn) && result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public boolean setRadio(int netType, boolean turnOn) {
|
|
enforceChangePermission();
|
|
if (!ConnectivityManager.isNetworkTypeValid(netType)) {
|
|
return false;
|
|
}
|
|
NetworkStateTracker tracker = mNetTrackers[netType];
|
|
return tracker != null && tracker.setRadio(turnOn);
|
|
}
|
|
|
|
public int startUsingNetworkFeature(int networkType, String feature) {
|
|
enforceChangePermission();
|
|
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
|
|
return -1;
|
|
}
|
|
NetworkStateTracker tracker = mNetTrackers[networkType];
|
|
if (tracker != null) {
|
|
return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int stopUsingNetworkFeature(int networkType, String feature) {
|
|
enforceChangePermission();
|
|
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
|
|
return -1;
|
|
}
|
|
NetworkStateTracker tracker = mNetTrackers[networkType];
|
|
if (tracker != null) {
|
|
return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Ensure that a network route exists to deliver traffic to the specified
|
|
* host via the specified network interface.
|
|
* @param networkType the type of the network over which traffic to the specified
|
|
* host is to be routed
|
|
* @param hostAddress the IP address of the host to which the route is desired
|
|
* @return {@code true} on success, {@code false} on failure
|
|
*/
|
|
public boolean requestRouteToHost(int networkType, int hostAddress) {
|
|
enforceChangePermission();
|
|
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
|
|
return false;
|
|
}
|
|
NetworkStateTracker tracker = mNetTrackers[networkType];
|
|
/*
|
|
* If there's only one connected network, and it's the one requested,
|
|
* then we don't have to do anything - the requested route already
|
|
* exists. If it's not the requested network, then it's not possible
|
|
* to establish the requested route. Finally, if there is more than
|
|
* one connected network, then we must insert an entry in the routing
|
|
* table.
|
|
*/
|
|
if (getNumConnectedNetworks() > 1) {
|
|
return tracker.requestRouteToHost(hostAddress);
|
|
} else {
|
|
return tracker.getNetworkInfo().getType() == networkType;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see ConnectivityManager#getBackgroundDataSetting()
|
|
*/
|
|
public boolean getBackgroundDataSetting() {
|
|
return Settings.Secure.getInt(mContext.getContentResolver(),
|
|
Settings.Secure.BACKGROUND_DATA, 1) == 1;
|
|
}
|
|
|
|
/**
|
|
* @see ConnectivityManager#setBackgroundDataSetting(boolean)
|
|
*/
|
|
public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) {
|
|
mContext.enforceCallingOrSelfPermission(
|
|
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
|
|
"ConnectivityService");
|
|
|
|
if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
|
|
|
|
Settings.Secure.putInt(mContext.getContentResolver(),
|
|
Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
|
|
|
|
Intent broadcast = new Intent(
|
|
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
|
|
mContext.sendBroadcast(broadcast);
|
|
}
|
|
|
|
private int getNumConnectedNetworks() {
|
|
int numConnectedNets = 0;
|
|
|
|
for (NetworkStateTracker nt : mNetTrackers) {
|
|
if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
|
|
++numConnectedNets;
|
|
}
|
|
}
|
|
return numConnectedNets;
|
|
}
|
|
|
|
private void enforceAccessPermission() {
|
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
|
|
"ConnectivityService");
|
|
}
|
|
|
|
private void enforceChangePermission() {
|
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
|
|
"ConnectivityService");
|
|
|
|
}
|
|
|
|
/**
|
|
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
|
|
* we ignore it. If it is for the active network, we send out a broadcast.
|
|
* But first, we check whether it might be possible to connect to a different
|
|
* network.
|
|
* @param info the {@code NetworkInfo} for the network
|
|
*/
|
|
private void handleDisconnect(NetworkInfo info) {
|
|
|
|
if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
|
|
|
|
mNetTrackers[info.getType()].setTeardownRequested(false);
|
|
/*
|
|
* If the disconnected network is not the active one, then don't report
|
|
* this as a loss of connectivity. What probably happened is that we're
|
|
* getting the disconnect for a network that we explicitly disabled
|
|
* in accordance with network preference policies.
|
|
*/
|
|
if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType())
|
|
return;
|
|
|
|
NetworkStateTracker newNet;
|
|
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
|
|
newNet = mWifiStateTracker;
|
|
} else /* info().getType() == TYPE_WIFI */ {
|
|
newNet = mMobileDataStateTracker;
|
|
}
|
|
|
|
/**
|
|
* See if the other network is available to fail over to.
|
|
* If is not available, we enable it anyway, so that it
|
|
* will be able to connect when it does become available,
|
|
* but we report a total loss of connectivity rather than
|
|
* report that we are attempting to fail over.
|
|
*/
|
|
NetworkInfo switchTo = null;
|
|
if (newNet.isAvailable()) {
|
|
mActiveNetwork = newNet;
|
|
switchTo = newNet.getNetworkInfo();
|
|
switchTo.setFailover(true);
|
|
if (!switchTo.isConnectedOrConnecting()) {
|
|
newNet.reconnect();
|
|
}
|
|
} else {
|
|
newNet.reconnect();
|
|
}
|
|
|
|
boolean otherNetworkConnected = false;
|
|
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
|
|
if (info.isFailover()) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
|
|
info.setFailover(false);
|
|
}
|
|
if (info.getReason() != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
|
|
}
|
|
if (info.getExtraInfo() != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
|
|
}
|
|
if (switchTo != null) {
|
|
otherNetworkConnected = switchTo.isConnected();
|
|
if (DBG) {
|
|
if (otherNetworkConnected) {
|
|
Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
|
|
} else {
|
|
Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
|
|
}
|
|
}
|
|
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
|
|
} else {
|
|
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
|
|
}
|
|
if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
|
|
(switchTo == null ? "" : " other=" + switchTo.getTypeName()));
|
|
|
|
mContext.sendStickyBroadcast(intent);
|
|
/*
|
|
* If the failover network is already connected, then immediately send out
|
|
* a followup broadcast indicating successful failover
|
|
*/
|
|
if (switchTo != null && otherNetworkConnected)
|
|
sendConnectedBroadcast(switchTo);
|
|
}
|
|
|
|
private void sendConnectedBroadcast(NetworkInfo info) {
|
|
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
|
|
if (info.isFailover()) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
|
|
info.setFailover(false);
|
|
}
|
|
if (info.getReason() != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
|
|
}
|
|
if (info.getExtraInfo() != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
|
|
}
|
|
mContext.sendStickyBroadcast(intent);
|
|
}
|
|
|
|
/**
|
|
* Called when an attempt to fail over to another network has failed.
|
|
* @param info the {@link NetworkInfo} for the failed network
|
|
*/
|
|
private void handleConnectionFailure(NetworkInfo info) {
|
|
mNetTrackers[info.getType()].setTeardownRequested(false);
|
|
if (getActiveNetworkInfo() == null) {
|
|
String reason = info.getReason();
|
|
String extraInfo = info.getExtraInfo();
|
|
|
|
if (DBG) {
|
|
String reasonText;
|
|
if (reason == null) {
|
|
reasonText = ".";
|
|
} else {
|
|
reasonText = " (" + reason + ").";
|
|
}
|
|
Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
|
|
}
|
|
|
|
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
|
|
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
|
|
if (reason != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
|
|
}
|
|
if (extraInfo != null) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
|
|
}
|
|
if (info.isFailover()) {
|
|
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
|
|
info.setFailover(false);
|
|
}
|
|
mContext.sendStickyBroadcast(intent);
|
|
}
|
|
}
|
|
|
|
private void handleConnect(NetworkInfo info) {
|
|
if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
|
|
|
|
// snapshot isFailover, because sendConnectedBroadcast() resets it
|
|
boolean isFailover = info.isFailover();
|
|
NetworkStateTracker thisNet = mNetTrackers[info.getType()];
|
|
NetworkStateTracker deadnet = null;
|
|
NetworkStateTracker otherNet;
|
|
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
|
|
otherNet = mWifiStateTracker;
|
|
} else /* info().getType() == TYPE_WIFI */ {
|
|
otherNet = mMobileDataStateTracker;
|
|
}
|
|
/*
|
|
* Check policy to see whether we are connected to a non-preferred
|
|
* network that now needs to be torn down.
|
|
*/
|
|
NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
|
|
NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
|
|
if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
|
|
if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
|
|
deadnet = mMobileDataStateTracker;
|
|
else
|
|
deadnet = mWifiStateTracker;
|
|
}
|
|
|
|
boolean toredown = false;
|
|
thisNet.setTeardownRequested(false);
|
|
if (!mTestMode && deadnet != null) {
|
|
if (DBG) Log.v(TAG, "Policy requires " +
|
|
deadnet.getNetworkInfo().getTypeName() + " teardown");
|
|
toredown = teardown(deadnet);
|
|
if (DBG && !toredown) {
|
|
Log.d(TAG, "Network declined teardown request");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note that if toredown is true, deadnet cannot be null, so there is
|
|
* no danger of a null pointer exception here..
|
|
*/
|
|
if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
|
|
mActiveNetwork = thisNet;
|
|
if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
|
|
thisNet.updateNetworkSettings();
|
|
sendConnectedBroadcast(info);
|
|
if (isFailover) {
|
|
otherNet.releaseWakeLock();
|
|
}
|
|
} else {
|
|
if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
|
|
info.getTypeName());
|
|
}
|
|
}
|
|
|
|
private void handleScanResultsAvailable(NetworkInfo info) {
|
|
int networkType = info.getType();
|
|
if (networkType != ConnectivityManager.TYPE_WIFI) {
|
|
if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
|
|
+ " Don't know how to handle.");
|
|
}
|
|
|
|
mNetTrackers[networkType].interpretScanResultsAvailable();
|
|
}
|
|
|
|
private void handleNotificationChange(boolean visible, int id, Notification notification) {
|
|
NotificationManager notificationManager = (NotificationManager) mContext
|
|
.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
if (visible) {
|
|
notificationManager.notify(id, notification);
|
|
} else {
|
|
notificationManager.cancel(id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* After any kind of change in the connectivity state of any network,
|
|
* make sure that anything that depends on the connectivity state of
|
|
* more than one network is set up correctly. We're mainly concerned
|
|
* with making sure that the list of DNS servers is set up according
|
|
* to which networks are connected, and ensuring that the right routing
|
|
* table entries exist.
|
|
*/
|
|
private void handleConnectivityChange() {
|
|
/*
|
|
* If both mobile and wifi are enabled, add the host routes that
|
|
* will allow MMS traffic to pass on the mobile network. But
|
|
* remove the default route for the mobile network, so that there
|
|
* will be only one default route, to ensure that all traffic
|
|
* except MMS will travel via Wi-Fi.
|
|
*/
|
|
int numConnectedNets = handleConfigurationChange();
|
|
if (numConnectedNets > 1) {
|
|
mMobileDataStateTracker.addPrivateRoutes();
|
|
mMobileDataStateTracker.removeDefaultRoute();
|
|
} else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
|
|
mMobileDataStateTracker.removePrivateRoutes();
|
|
mMobileDataStateTracker.restoreDefaultRoute();
|
|
}
|
|
}
|
|
|
|
private int handleConfigurationChange() {
|
|
/*
|
|
* Set DNS properties. Always put Wi-Fi entries at the front of
|
|
* the list if it is active.
|
|
*/
|
|
int index = 1;
|
|
String lastDns = "";
|
|
int numConnectedNets = 0;
|
|
int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
|
|
int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
|
|
|
|
for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
|
|
NetworkStateTracker nt = mNetTrackers[netType];
|
|
if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
|
|
++numConnectedNets;
|
|
String[] dnsList = nt.getNameServers();
|
|
for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
|
|
// skip duplicate entries
|
|
if (!dnsList[i].equals(lastDns)) {
|
|
SystemProperties.set("net.dns" + index++, dnsList[i]);
|
|
lastDns = dnsList[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Null out any DNS properties that are no longer used
|
|
for (int i = index; i <= mNumDnsEntries; i++) {
|
|
SystemProperties.set("net.dns" + i, "");
|
|
}
|
|
mNumDnsEntries = index - 1;
|
|
// Notify the name resolver library of the change
|
|
SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
|
|
return numConnectedNets;
|
|
}
|
|
|
|
@Override
|
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
|
!= PackageManager.PERMISSION_GRANTED) {
|
|
pw.println("Permission Denial: can't dump ConnectivityService from from pid="
|
|
+ Binder.getCallingPid()
|
|
+ ", uid=" + Binder.getCallingUid());
|
|
return;
|
|
}
|
|
if (mActiveNetwork == null) {
|
|
pw.println("No active network");
|
|
} else {
|
|
pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
|
|
}
|
|
pw.println();
|
|
for (NetworkStateTracker nst : mNetTrackers) {
|
|
pw.println(nst.getNetworkInfo());
|
|
pw.println(nst);
|
|
pw.println();
|
|
}
|
|
}
|
|
|
|
private class MyHandler extends Handler {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
NetworkInfo info;
|
|
switch (msg.what) {
|
|
case NetworkStateTracker.EVENT_STATE_CHANGED:
|
|
info = (NetworkInfo) msg.obj;
|
|
if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
|
|
info.getState() + "/" + info.getDetailedState());
|
|
|
|
// Connectivity state changed:
|
|
// [31-13] Reserved for future use
|
|
// [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
|
|
// [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
|
|
// [2-0] Network type (as defined by ConnectivityManager)
|
|
int eventLogParam = (info.getType() & 0x7) |
|
|
((info.getDetailedState().ordinal() & 0x3f) << 3) |
|
|
(info.getSubtype() << 9);
|
|
EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
|
|
|
|
if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
|
|
handleConnectionFailure(info);
|
|
} else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
|
|
handleDisconnect(info);
|
|
} else if (info.getState() == NetworkInfo.State.SUSPENDED) {
|
|
// TODO: need to think this over.
|
|
// the logic here is, handle SUSPENDED the same as DISCONNECTED. The
|
|
// only difference being we are broadcasting an intent with NetworkInfo
|
|
// that's suspended. This allows the applications an opportunity to
|
|
// handle DISCONNECTED and SUSPENDED differently, or not.
|
|
handleDisconnect(info);
|
|
} else if (info.getState() == NetworkInfo.State.CONNECTED) {
|
|
handleConnect(info);
|
|
}
|
|
handleConnectivityChange();
|
|
break;
|
|
|
|
case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
|
|
info = (NetworkInfo) msg.obj;
|
|
handleScanResultsAvailable(info);
|
|
break;
|
|
|
|
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
|
|
handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
|
|
|
|
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
|
|
handleConfigurationChange();
|
|
break;
|
|
|
|
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
|
|
// fill me in
|
|
break;
|
|
|
|
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
|
|
// fill me in
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|