diff options
Diffstat (limited to 'services/java/com/android/server/ConnectivityService.java')
| -rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 620 |
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"); + } + } } |
