summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/ConnectivityService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/ConnectivityService.java')
-rw-r--r--services/java/com/android/server/ConnectivityService.java620
1 files changed, 478 insertions, 142 deletions
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 86ada40..5c7a3ed 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,25 +16,38 @@
package com.android.server;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_DUMMY;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.CaptivePortalTracker;
import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -64,11 +77,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -76,21 +93,24 @@ import android.util.SparseIntArray;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
+import com.android.server.net.BaseNetworkObserver;
+import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+
import dalvik.system.DexClassLoader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -106,13 +126,15 @@ import java.util.List;
* @hide
*/
public class ConnectivityService extends IConnectivityManager.Stub {
+ private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private static final String TAG = "ConnectivityService";
private static final boolean LOGD_RULES = false;
+ // TODO: create better separation between radio types and network types
+
// how long to wait before switching back to a radio's default network
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
// system property that can override the above value
@@ -126,7 +148,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
+ private KeyStore mKeyStore;
+
private Vpn mVpn;
+ private VpnCallback mVpnCallback = new VpnCallback();
+
+ private boolean mLockdownEnabled;
+ private LockdownVpnTracker mLockdownTracker;
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
private Object mRulesLock = new Object();
@@ -142,6 +170,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private NetworkStateTracker mNetTrackers[];
+ /* Handles captive portal check on a network */
+ private CaptivePortalTracker mCaptivePortalTracker;
+
/**
* The link properties that define the current links
*/
@@ -186,95 +217,83 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final boolean TO_DEFAULT_TABLE = true;
private static final boolean TO_SECONDARY_TABLE = false;
- // Share the event space with NetworkStateTracker (which can't see this
- // internal class but sends us events). If you change these, change
- // NetworkStateTracker.java too.
- private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
- private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
-
/**
* used internally as a delayed event to make us switch back to the
* default network
*/
- private static final int EVENT_RESTORE_DEFAULT_NETWORK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+ private static final int EVENT_RESTORE_DEFAULT_NETWORK = 1;
/**
* used internally to change our mobile data enabled flag
*/
- private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
- MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+ private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
/**
* used internally to change our network preference setting
* arg1 = networkType to prefer
*/
- private static final int EVENT_SET_NETWORK_PREFERENCE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+ private static final int EVENT_SET_NETWORK_PREFERENCE = 3;
/**
* used internally to synchronize inet condition reports
* arg1 = networkType
* arg2 = condition (0 bad, 100 good)
*/
- private static final int EVENT_INET_CONDITION_CHANGE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+ private static final int EVENT_INET_CONDITION_CHANGE = 4;
/**
* used internally to mark the end of inet condition hold periods
* arg1 = networkType
*/
- private static final int EVENT_INET_CONDITION_HOLD_END =
- MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+ private static final int EVENT_INET_CONDITION_HOLD_END = 5;
/**
* used internally to set enable/disable cellular data
* arg1 = ENBALED or DISABLED
*/
- private static final int EVENT_SET_MOBILE_DATA =
- MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+ private static final int EVENT_SET_MOBILE_DATA = 7;
/**
* used internally to clear a wakelock when transitioning
* from one net to another
*/
- private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+ private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
/**
* used internally to reload global proxy settings
*/
- private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
- MAX_NETWORK_STATE_TRACKER_EVENT + 9;
+ private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
/**
* used internally to set external dependency met/unmet
* arg1 = ENABLED (met) or DISABLED (unmet)
* arg2 = NetworkType
*/
- private static final int EVENT_SET_DEPENDENCY_MET =
- MAX_NETWORK_STATE_TRACKER_EVENT + 10;
+ private static final int EVENT_SET_DEPENDENCY_MET = 10;
/**
* used internally to restore DNS properties back to the
* default network
*/
- private static final int EVENT_RESTORE_DNS =
- MAX_NETWORK_STATE_TRACKER_EVENT + 11;
+ private static final int EVENT_RESTORE_DNS = 11;
/**
* used internally to send a sticky broadcast delayed.
*/
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
- MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+ private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 12;
/**
* Used internally to
* {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
*/
- private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = 13;
- private Handler mHandler;
+ private static final int EVENT_VPN_STATE_CHANGED = 14;
+
+ /** Handler used for internal events. */
+ private InternalHandler mHandler;
+ /** Handler used for incoming {@link NetworkStateTracker} events. */
+ private NetworkStateTrackerHandler mTrackerHandler;
// list of DeathRecipients used to make sure features are turned off when
// a process dies
@@ -328,11 +347,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ // Currently, omitting a NetworkFactory will create one internally
+ // TODO: create here when we have cleaner WiMAX support
+ this(context, netd, statsService, policyManager, null);
+ }
+
+ public ConnectivityService(Context context, INetworkManagementService netManager,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
- mHandler = new MyHandler(handlerThread.getLooper());
+ mHandler = new InternalHandler(handlerThread.getLooper());
+ mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
+
+ if (netFactory == null) {
+ netFactory = new DefaultNetworkFactory(context, mTrackerHandler);
+ }
// setup our unique device name
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
@@ -358,8 +390,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
mContext = checkNotNull(context, "missing Context");
- mNetd = checkNotNull(netd, "missing INetworkManagementService");
+ mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
+ mKeyStore = KeyStore.getInstance();
try {
mPolicyManager.registerListener(mPolicyListener);
@@ -472,69 +505,38 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- /*
- * 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.
- */
- for (int netType : mPriorityList) {
- switch (mNetConfigs[netType].radio) {
- case ConnectivityManager.TYPE_WIFI:
- mNetTrackers[netType] = new WifiStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_DUMMY:
- mNetTrackers[netType] = new DummyDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_BLUETOOTH:
- mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_WIMAX:
- mNetTrackers[netType] = makeWimaxStateTracker();
- if (mNetTrackers[netType]!= null) {
- mNetTrackers[netType].startMonitoring(context, mHandler);
- }
- break;
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- default:
- loge("Trying to create a DataStateTracker for an unknown radio type " +
- mNetConfigs[netType].radio);
+
+ // Create and start trackers for hard-coded networks
+ for (int targetNetworkType : mPriorityList) {
+ final NetworkConfig config = mNetConfigs[targetNetworkType];
+ final NetworkStateTracker tracker;
+ try {
+ tracker = netFactory.createTracker(targetNetworkType, config);
+ mNetTrackers[targetNetworkType] = tracker;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ + " tracker: " + e);
continue;
}
- mCurrentLinkProperties[netType] = null;
- if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
- mNetTrackers[netType].reconnect();
+
+ tracker.startMonitoring(context, mTrackerHandler);
+ if (config.isDefault()) {
+ tracker.reconnect();
}
}
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
-
- mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceTypes().length != 0);
- mVpn = new Vpn(mContext, new VpnCallback());
+ mVpn = new Vpn(mContext, mVpnCallback, mNetd);
+ mVpn.startMonitoring(mContext, mTrackerHandler);
try {
- nmService.registerObserver(mTethering);
- nmService.registerObserver(mVpn);
+ mNetd.registerObserver(mTethering);
+ mNetd.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -548,8 +550,55 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loadGlobalProxy();
}
-private NetworkStateTracker makeWimaxStateTracker() {
- //Initialize Wimax
+
+ /**
+ * Factory that creates {@link NetworkStateTracker} instances using given
+ * {@link NetworkConfig}.
+ */
+ public interface NetworkFactory {
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
+ }
+
+ private static class DefaultNetworkFactory implements NetworkFactory {
+ private final Context mContext;
+ private final Handler mTrackerHandler;
+
+ public DefaultNetworkFactory(Context context, Handler trackerHandler) {
+ mContext = context;
+ mTrackerHandler = trackerHandler;
+ }
+
+ @Override
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
+ switch (config.radio) {
+ case TYPE_WIFI:
+ return new WifiStateTracker(targetNetworkType, config.name);
+ case TYPE_MOBILE:
+ return new MobileDataStateTracker(targetNetworkType, config.name);
+ case TYPE_DUMMY:
+ return new DummyDataStateTracker(targetNetworkType, config.name);
+ case TYPE_BLUETOOTH:
+ return BluetoothTetheringDataTracker.getInstance();
+ case TYPE_WIMAX:
+ return makeWimaxStateTracker(mContext, mTrackerHandler);
+ case TYPE_ETHERNET:
+ return EthernetDataTracker.getInstance();
+ default:
+ throw new IllegalArgumentException(
+ "Trying to create a NetworkStateTracker for an unknown radio type: "
+ + config.radio);
+ }
+ }
+ }
+
+ /**
+ * Loads external WiMAX library and registers as system service, returning a
+ * {@link NetworkStateTracker} for WiMAX. Caller is still responsible for
+ * invoking {@link NetworkStateTracker#startMonitoring(Context, Handler)}.
+ */
+ private static NetworkStateTracker makeWimaxStateTracker(
+ Context context, Handler trackerHandler) {
+ // Initialize Wimax
DexClassLoader wimaxClassLoader;
Class wimaxStateTrackerClass = null;
Class wimaxServiceClass = null;
@@ -562,25 +611,25 @@ private NetworkStateTracker makeWimaxStateTracker() {
NetworkStateTracker wimaxStateTracker = null;
- boolean isWimaxEnabled = mContext.getResources().getBoolean(
+ boolean isWimaxEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_wimaxEnabled);
if (isWimaxEnabled) {
try {
- wimaxJarLocation = mContext.getResources().getString(
+ wimaxJarLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceJarLocation);
- wimaxLibLocation = mContext.getResources().getString(
+ wimaxLibLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxNativeLibLocation);
- wimaxManagerClassName = mContext.getResources().getString(
+ wimaxManagerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxManagerClassname);
- wimaxServiceClassName = mContext.getResources().getString(
+ wimaxServiceClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceClassname);
- wimaxStateTrackerClassName = mContext.getResources().getString(
+ wimaxStateTrackerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxStateTrackerClassname);
log("wimaxJarLocation: " + wimaxJarLocation);
wimaxClassLoader = new DexClassLoader(wimaxJarLocation,
- new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ new ContextWrapper(context).getCacheDir().getAbsolutePath(),
wimaxLibLocation, ClassLoader.getSystemClassLoader());
try {
@@ -601,13 +650,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor
(new Class[] {Context.class, Handler.class});
- wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext,
- mHandler);
+ wimaxStateTracker = (NetworkStateTracker) wmxStTrkrConst.newInstance(
+ context, trackerHandler);
Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor
(new Class[] {Context.class, wimaxStateTrackerClass});
wmxSrvConst.setAccessible(true);
- IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker);
+ IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(context, wimaxStateTracker);
wmxSrvConst.setAccessible(false);
ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker);
@@ -623,6 +672,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
return wimaxStateTracker;
}
+
/**
* Sets the preferred network.
* @param preference the new preference
@@ -630,7 +680,8 @@ private NetworkStateTracker makeWimaxStateTracker() {
public void setNetworkPreference(int preference) {
enforceChangePermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
}
public int getNetworkPreference() {
@@ -749,6 +800,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
}
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
return info;
}
@@ -766,6 +820,17 @@ private NetworkStateTracker makeWimaxStateTracker() {
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ public NetworkInfo getActiveNetworkInfoUnfiltered() {
+ enforceAccessPermission();
+ if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+ final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+ if (tracker != null) {
+ return tracker.getNetworkInfo();
+ }
+ }
+ return null;
+ }
+
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
enforceConnectivityInternalPermission();
@@ -923,6 +988,14 @@ private NetworkStateTracker makeWimaxStateTracker() {
return tracker != null && tracker.setRadio(turnOn);
}
+ private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
+ @Override
+ public void interfaceClassDataActivityChanged(String label, boolean active) {
+ int deviceType = Integer.parseInt(label);
+ sendDataActivityBroadcast(deviceType, active);
+ }
+ };
+
/**
* Used to notice when the calling process dies so we can self-expire
*
@@ -1017,6 +1090,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
// TODO - move this into individual networktrackers
int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
+ if (mLockdownEnabled) {
+ // Since carrier APNs usually aren't available from VPN
+ // endpoint, mark them as unavailable.
+ return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
+ }
+
if (mProtectedNetworks.contains(usedNetworkType)) {
enforceConnectivityInternalPermission();
}
@@ -1291,8 +1370,10 @@ private NetworkStateTracker makeWimaxStateTracker() {
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
+ DetailedState netState = tracker.getNetworkInfo().getDetailedState();
- if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
+ if (tracker == null || (netState != DetailedState.CONNECTED &&
+ netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
tracker.isTeardownRequested()) {
if (VDBG) {
log("requestRouteToHostAddress on down network " +
@@ -1591,6 +1672,10 @@ private NetworkStateTracker makeWimaxStateTracker() {
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+
+ // Remove idletimer previously setup in {@code handleConnect}
+ removeDataActivityTracking(prevNetType);
+
/*
* 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
@@ -1610,6 +1695,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1719,7 +1805,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
- private void sendConnectedBroadcast(NetworkInfo info) {
+ public void sendConnectedBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -1734,8 +1820,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
+
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1759,6 +1850,19 @@ private NetworkStateTracker makeWimaxStateTracker() {
sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
}
+ private void sendDataActivityBroadcast(int deviceType, boolean active) {
+ Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
+ intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
+ intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+ RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Called when an attempt to fail over to another network has failed.
* @param info the {@link NetworkInfo} for the failed network
@@ -1779,6 +1883,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
@@ -1829,7 +1934,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
log("sendStickyBroadcast: action=" + intent.getAction());
}
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -1850,37 +1960,55 @@ private NetworkStateTracker makeWimaxStateTracker() {
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
- mContext.sendStickyBroadcast(mInitialBroadcast);
+ mContext.sendStickyBroadcastAsUser(mInitialBroadcast, UserHandle.ALL);
mInitialBroadcast = null;
}
}
// load the global proxy at startup
mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
+
+ // Try bringing up tracker, but if KeyStore isn't ready yet, wait
+ // for user to unlock device.
+ if (!updateLockdownVpn()) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiver(mUserPresentReceiver, filter);
+ }
+ }
+
+ private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Try creating lockdown tracker, since user present usually means
+ // unlocked keystore.
+ if (updateLockdownVpn()) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+ };
+
+ private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
+ if ((type != mNetworkPreference &&
+ mNetConfigs[mActiveDefaultNetwork].priority >
+ mNetConfigs[type].priority) ||
+ mNetworkPreference == mActiveDefaultNetwork) return false;
+ return true;
}
private void handleConnect(NetworkInfo info) {
- final int type = info.getType();
+ final int newNetType = info.getType();
+
+ setupDataActivityTracking(newNetType);
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
- final NetworkStateTracker thisNet = mNetTrackers[type];
+ final NetworkStateTracker thisNet = mNetTrackers[newNetType];
+ final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
- if (mNetConfigs[type].isDefault()) {
- if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
- if ((type != mNetworkPreference &&
- mNetConfigs[mActiveDefaultNetwork].priority >
- mNetConfigs[type].priority) ||
- mNetworkPreference == mActiveDefaultNetwork) {
- // don't accept this one
- if (VDBG) {
- log("Not broadcasting CONNECT_ACTION " +
- "to torn down network " + info.getTypeName());
- }
- teardown(thisNet);
- return;
- } else {
+ if (mNetConfigs[newNetType].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
+ if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork];
@@ -1893,6 +2021,14 @@ private NetworkStateTracker makeWimaxStateTracker() {
teardown(thisNet);
return;
}
+ } else {
+ // don't accept this one
+ if (VDBG) {
+ log("Not broadcasting CONNECT_ACTION " +
+ "to torn down network " + info.getTypeName());
+ }
+ teardown(thisNet);
+ return;
}
}
synchronized (ConnectivityService.this) {
@@ -1906,7 +2042,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
1000);
}
}
- mActiveDefaultNetwork = type;
+ mActiveDefaultNetwork = newNetType;
// this will cause us to come up initially as unconnected and switching
// to connected after our normal pause unless somebody reports us as reall
// disconnected
@@ -1918,20 +2054,99 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
thisNet.setTeardownRequested(false);
updateNetworkSettings(thisNet);
- handleConnectivityChange(type, false);
+ handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
- final String iface = thisNet.getLinkProperties().getInterfaceName();
- if (iface != null) {
+ if (thisIface != null) {
try {
- BatteryStatsService.getService().noteNetworkInterfaceType(iface, type);
+ BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, newNetType);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
}
+ private void handleCaptivePortalTrackerCheck(NetworkInfo info) {
+ if (DBG) log("Captive portal check " + info);
+ int type = info.getType();
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ if (mNetConfigs[type].isDefault()) {
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+ if (isNewNetTypePreferredOverCurrentNetType(type)) {
+ if (DBG) log("Captive check on " + info.getTypeName());
+ mCaptivePortalTracker = CaptivePortalTracker.detect(mContext, info,
+ ConnectivityService.this);
+ return;
+ } else {
+ if (DBG) log("Tear down low priority net " + info.getTypeName());
+ teardown(thisNet);
+ return;
+ }
+ }
+ }
+
+ thisNet.captivePortalCheckComplete();
+ }
+
+ /** @hide */
+ public void captivePortalCheckComplete(NetworkInfo info) {
+ mNetTrackers[info.getType()].captivePortalCheckComplete();
+ mCaptivePortalTracker = null;
+ }
+
+ /**
+ * Setup data activity tracking for the given network interface.
+ *
+ * Every {@code setupDataActivityTracking} should be paired with a
+ * {@link removeDataActivityTracking} for cleanup.
+ */
+ private void setupDataActivityTracking(int type) {
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String iface = thisNet.getLinkProperties().getInterfaceName();
+
+ final int timeout;
+
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE,
+ 0);
+ // Canonicalize mobile network type
+ type = ConnectivityManager.TYPE_MOBILE;
+ } else if (ConnectivityManager.TYPE_WIFI == type) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI,
+ 0);
+ } else {
+ // do not track any other networks
+ timeout = 0;
+ }
+
+ if (timeout > 0 && iface != null) {
+ try {
+ mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Remove data activity tracking when network disconnects.
+ */
+ private void removeDataActivityTracking(int type) {
+ final NetworkStateTracker net = mNetTrackers[type];
+ final String iface = net.getLinkProperties().getInterfaceName();
+
+ if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
+ ConnectivityManager.TYPE_WIFI == type)) {
+ try {
+ // the call fails silently if no idletimer setup for this interface
+ mNetd.removeIdleTimer(iface);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* After a change in the connectivity state of a network. We're mainly
* concerned with making sure that the list of DNS servers is set up
@@ -2136,9 +2351,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
public void updateNetworkSettings(NetworkStateTracker nt) {
String key = nt.getTcpBufferSizesPropName();
- String bufferSizes = SystemProperties.get(key);
+ String bufferSizes = key == null ? null : SystemProperties.get(key);
- if (bufferSizes.length() == 0) {
+ if (TextUtils.isEmpty(bufferSizes)) {
if (VDBG) log(key + " not found in system properties. Using defaults");
// Setting to default values so we won't be stuck to previous values
@@ -2264,7 +2479,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
* Connectivity events can happen before boot has completed ...
*/
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
// Caller must grab mDnsLock.
@@ -2374,7 +2594,8 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2383,20 +2604,28 @@ private NetworkStateTracker makeWimaxStateTracker() {
Binder.getCallingUid());
return;
}
+
+ // TODO: add locking to get atomic snapshot
pw.println();
- for (NetworkStateTracker nst : mNetTrackers) {
+ for (int i = 0; i < mNetTrackers.length; i++) {
+ final NetworkStateTracker nst = mNetTrackers[i];
if (nst != null) {
+ pw.println("NetworkStateTracker for " + getNetworkTypeName(i) + ":");
+ pw.increaseIndent();
if (nst.getNetworkInfo().isConnected()) {
pw.println("Active network: " + nst.getNetworkInfo().
getTypeName());
}
pw.println(nst.getNetworkInfo());
+ pw.println(nst.getLinkProperties());
pw.println(nst);
pw.println();
+ pw.decreaseIndent();
}
}
pw.println("Network Requester Pids:");
+ pw.increaseIndent();
for (int net : mPriorityList) {
String pidString = net + ": ";
for (Object pid : mNetRequestersPids[net]) {
@@ -2405,12 +2634,15 @@ private NetworkStateTracker makeWimaxStateTracker() {
pw.println(pidString);
}
pw.println();
+ pw.decreaseIndent();
pw.println("FeatureUsers:");
+ pw.increaseIndent();
for (Object requester : mFeatureUsers) {
pw.println(requester.toString());
}
pw.println();
+ pw.decreaseIndent();
synchronized (this) {
pw.println("NetworkTranstionWakeLock is currently " +
@@ -2424,15 +2656,17 @@ private NetworkStateTracker makeWimaxStateTracker() {
if (mInetLog != null) {
pw.println();
pw.println("Inet condition reports:");
+ pw.increaseIndent();
for(int i = 0; i < mInetLog.size(); i++) {
pw.println(mInetLog.get(i));
}
+ pw.decreaseIndent();
}
}
// must be stateless - things change under us.
- private class MyHandler extends Handler {
- public MyHandler(Looper looper) {
+ private class NetworkStateTrackerHandler extends Handler {
+ public NetworkStateTrackerHandler(Looper looper) {
super(looper);
}
@@ -2468,6 +2702,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
if (info.getDetailedState() ==
NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
+ } else if (info.getDetailedState() ==
+ DetailedState.CAPTIVE_PORTAL_CHECK) {
+ handleCaptivePortalTrackerCheck(info);
} else if (state == NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (state == NetworkInfo.State.SUSPENDED) {
@@ -2482,6 +2719,9 @@ private NetworkStateTracker makeWimaxStateTracker() {
} else if (state == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onNetworkInfoChanged(info);
+ }
break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
info = (NetworkInfo) msg.obj;
@@ -2495,6 +2735,19 @@ private NetworkStateTracker makeWimaxStateTracker() {
type = info.getType();
updateNetworkSettings(mNetTrackers[type]);
break;
+ }
+ }
+ }
+
+ private class InternalHandler extends Handler {
+ public InternalHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ NetworkInfo info;
+ switch (msg.what) {
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
synchronized (ConnectivityService.this) {
@@ -2566,6 +2819,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
final int networkType = msg.arg1;
final boolean enabled = msg.arg2 == ENABLED;
handleSetPolicyDataEnable(networkType, enabled);
+ break;
+ }
+ case EVENT_VPN_STATE_CHANGED: {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
+ }
+ break;
}
}
}
@@ -2883,7 +3143,12 @@ private NetworkStateTracker makeWimaxStateTracker() {
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- mContext.sendStickyBroadcast(intent);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private static class SettingsObserver extends ContentObserver {
@@ -2907,11 +3172,11 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
- private void log(String s) {
+ private static void log(String s) {
Slog.d(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Slog.e(TAG, s);
}
@@ -2964,6 +3229,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean protectVpn(ParcelFileDescriptor socket) {
+ throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
if (ConnectivityManager.isNetworkTypeValid(type)) {
@@ -2990,6 +3256,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
+ throwIfLockdownEnabled();
return mVpn.prepare(oldPackage, newPackage);
}
@@ -3002,18 +3269,22 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
+ throwIfLockdownEnabled();
return mVpn.establish(config);
}
/**
- * Start legacy VPN and return an intent to VpnDialogs. This method is
- * used by VpnSettings and not available in ConnectivityManager.
- * Permissions are checked in Vpn class.
- * @hide
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
*/
@Override
- public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- mVpn.startLegacyVpn(config, racoon, mtpd);
+ public void startLegacyVpn(VpnProfile profile) {
+ throwIfLockdownEnabled();
+ final LinkProperties egress = getActiveLinkProperties();
+ if (egress == null) {
+ throw new IllegalStateException("Missing active network connection");
+ }
+ mVpn.startLegacyVpn(profile, mKeyStore, egress);
}
/**
@@ -3024,6 +3295,7 @@ private NetworkStateTracker makeWimaxStateTracker() {
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
+ throwIfLockdownEnabled();
return mVpn.getLegacyVpnInfo();
}
@@ -3038,10 +3310,13 @@ private NetworkStateTracker makeWimaxStateTracker() {
* be done whenever a better abstraction is developed.
*/
public class VpnCallback {
-
private VpnCallback() {
}
+ public void onStateChanged(NetworkInfo info) {
+ mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
+ }
+
public void override(List<String> dnsServers, List<String> searchDomains) {
if (dnsServers == null) {
restore();
@@ -3108,4 +3383,65 @@ private NetworkStateTracker makeWimaxStateTracker() {
}
}
}
+
+ @Override
+ public boolean updateLockdownVpn() {
+ enforceSystemUid();
+
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
+ Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker");
+ return false;
+ }
+
+ final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+ } else {
+ setLockdownTracker(null);
+ }
+
+ return true;
+ }
+
+ /**
+ * Internally set new {@link LockdownVpnTracker}, shutting down any existing
+ * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
+ */
+ private void setLockdownTracker(LockdownVpnTracker tracker) {
+ // Shutdown any existing tracker
+ final LockdownVpnTracker existing = mLockdownTracker;
+ mLockdownTracker = null;
+ if (existing != null) {
+ existing.shutdown();
+ }
+
+ try {
+ if (tracker != null) {
+ mNetd.setFirewallEnabled(true);
+ mLockdownTracker = tracker;
+ mLockdownTracker.init();
+ } else {
+ mNetd.setFirewallEnabled(false);
+ }
+ } catch (RemoteException e) {
+ // ignored; NMS lives inside system_server
+ }
+ }
+
+ private void throwIfLockdownEnabled() {
+ if (mLockdownEnabled) {
+ throw new IllegalStateException("Unavailable in lockdown mode");
+ }
+ }
+
+ private static void enforceSystemUid() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only available to AID_SYSTEM");
+ }
+ }
}