Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.server.ethernet; |
| 18 | |
| 19 | import android.content.Context; |
| 20 | import android.net.ConnectivityManager; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 21 | import android.net.DhcpResults; |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 22 | import android.net.EthernetManager; |
| 23 | import android.net.IEthernetServiceListener; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 24 | import android.net.InterfaceConfiguration; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 25 | import android.net.IpConfiguration; |
| 26 | import android.net.IpConfiguration.IpAssignment; |
| 27 | import android.net.IpConfiguration.ProxySettings; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 28 | import android.net.LinkProperties; |
| 29 | import android.net.NetworkAgent; |
| 30 | import android.net.NetworkCapabilities; |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 31 | import android.net.NetworkFactory; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 32 | import android.net.NetworkInfo; |
| 33 | import android.net.NetworkInfo.DetailedState; |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 34 | import android.net.NetworkUtils; |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 35 | import android.net.StaticIpConfiguration; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 36 | import android.os.Handler; |
| 37 | import android.os.IBinder; |
| 38 | import android.os.INetworkManagementService; |
| 39 | import android.os.Looper; |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 40 | import android.os.RemoteCallbackList; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 41 | import android.os.RemoteException; |
| 42 | import android.os.ServiceManager; |
| 43 | import android.text.TextUtils; |
| 44 | import android.util.Log; |
| 45 | |
Lorenzo Colitti | aea43f5 | 2014-08-19 11:45:46 -0700 | [diff] [blame] | 46 | import com.android.internal.util.IndentingPrintWriter; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 47 | import com.android.server.net.BaseNetworkObserver; |
| 48 | |
Lorenzo Colitti | aea43f5 | 2014-08-19 11:45:46 -0700 | [diff] [blame] | 49 | import java.io.FileDescriptor; |
| 50 | import java.io.PrintWriter; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 51 | |
| 52 | |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 53 | /** |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 54 | * Manages connectivity for an Ethernet interface. |
| 55 | * |
| 56 | * Ethernet Interfaces may be present at boot time or appear after boot (e.g., |
| 57 | * for Ethernet adapters connected over USB). This class currently supports |
| 58 | * only one interface. When an interface appears on the system (or is present |
| 59 | * at boot time) this class will start tracking it and bring it up, and will |
| 60 | * attempt to connect when requested. Any other interfaces that subsequently |
| 61 | * appear will be ignored until the tracked interface disappears. Only |
| 62 | * interfaces whose names match the <code>config_ethernet_iface_regex</code> |
| 63 | * regular expression are tracked. |
| 64 | * |
| 65 | * This class reports a static network score of 70 when it is tracking an |
| 66 | * interface and that interface's link is up, and a score of 0 otherwise. |
| 67 | * |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 68 | * @hide |
| 69 | */ |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 70 | class EthernetNetworkFactory { |
| 71 | private static final String NETWORK_TYPE = "Ethernet"; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 72 | private static final String TAG = "EthernetNetworkFactory"; |
| 73 | private static final int NETWORK_SCORE = 70; |
| 74 | private static final boolean DBG = true; |
| 75 | |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 76 | /** Tracks interface changes. Called from NetworkManagementService. */ |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 77 | private InterfaceObserver mInterfaceObserver; |
| 78 | |
| 79 | /** For static IP configuration */ |
| 80 | private EthernetManager mEthernetManager; |
| 81 | |
| 82 | /** To set link state and configure IP addresses. */ |
| 83 | private INetworkManagementService mNMService; |
| 84 | |
| 85 | /* To communicate with ConnectivityManager */ |
| 86 | private NetworkCapabilities mNetworkCapabilities; |
| 87 | private NetworkAgent mNetworkAgent; |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 88 | private LocalNetworkFactory mFactory; |
| 89 | private Context mContext; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 90 | |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 91 | /** Product-dependent regular expression of interface names we track. */ |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 92 | private static String mIfaceMatch = ""; |
| 93 | |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 94 | /** To notify Ethernet status. */ |
| 95 | private final RemoteCallbackList<IEthernetServiceListener> mListeners; |
| 96 | |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 97 | /** Data members. All accesses to these must be synchronized(this). */ |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 98 | private static String mIface = ""; |
| 99 | private String mHwAddr; |
| 100 | private static boolean mLinkUp; |
| 101 | private NetworkInfo mNetworkInfo; |
| 102 | private LinkProperties mLinkProperties; |
| 103 | |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 104 | EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) { |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 105 | mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); |
| 106 | mLinkProperties = new LinkProperties(); |
| 107 | initNetworkCapabilities(); |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 108 | mListeners = listeners; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 111 | private class LocalNetworkFactory extends NetworkFactory { |
| 112 | LocalNetworkFactory(String name, Context context, Looper looper) { |
| 113 | super(looper, context, name, new NetworkCapabilities()); |
| 114 | } |
| 115 | |
| 116 | protected void startNetwork() { |
| 117 | onRequestNetwork(); |
| 118 | } |
| 119 | protected void stopNetwork() { |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 120 | } |
| 121 | } |
| 122 | |
| 123 | |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 124 | /** |
| 125 | * Updates interface state variables. |
| 126 | * Called on link state changes or on startup. |
| 127 | */ |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 128 | private void updateInterfaceState(String iface, boolean up) { |
| 129 | if (!mIface.equals(iface)) { |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 130 | return; |
| 131 | } |
| 132 | Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); |
| 133 | |
| 134 | synchronized(this) { |
| 135 | mLinkUp = up; |
| 136 | mNetworkInfo.setIsAvailable(up); |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 137 | if (!up) { |
| 138 | // Tell the agent we're disconnected. It will call disconnect(). |
| 139 | mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); |
| 140 | } |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 141 | updateAgent(); |
Robert Greenwalt | e3cbf2e | 2014-09-10 10:06:32 -0700 | [diff] [blame] | 142 | // set our score lower than any network could go |
| 143 | // so we get dropped. TODO - just unregister the factory |
| 144 | // when link goes down. |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 145 | mFactory.setScoreFilter(up ? NETWORK_SCORE : -1); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | |
| 149 | private class InterfaceObserver extends BaseNetworkObserver { |
| 150 | @Override |
| 151 | public void interfaceLinkStateChanged(String iface, boolean up) { |
| 152 | updateInterfaceState(iface, up); |
| 153 | } |
| 154 | |
| 155 | @Override |
| 156 | public void interfaceAdded(String iface) { |
| 157 | maybeTrackInterface(iface); |
| 158 | } |
| 159 | |
| 160 | @Override |
| 161 | public void interfaceRemoved(String iface) { |
| 162 | stopTrackingInterface(iface); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | private void setInterfaceUp(String iface) { |
| 167 | // Bring up the interface so we get link status indications. |
| 168 | try { |
Pierre Couillaud | ef8e0b9 | 2015-02-11 08:55:29 -0800 | [diff] [blame] | 169 | NetworkUtils.stopDhcp(iface); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 170 | mNMService.setInterfaceUp(iface); |
| 171 | String hwAddr = null; |
| 172 | InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); |
| 173 | |
| 174 | if (config == null) { |
| 175 | Log.e(TAG, "Null iterface config for " + iface + ". Bailing out."); |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | synchronized (this) { |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 180 | if (!isTrackingInterface()) { |
| 181 | setInterfaceInfoLocked(iface, config.getHardwareAddress()); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 182 | mNetworkInfo.setIsAvailable(true); |
| 183 | mNetworkInfo.setExtraInfo(mHwAddr); |
| 184 | } else { |
| 185 | Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface); |
| 186 | mNMService.setInterfaceDown(iface); |
| 187 | } |
| 188 | } |
| 189 | } catch (RemoteException e) { |
| 190 | Log.e(TAG, "Error upping interface " + mIface + ": " + e); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | private boolean maybeTrackInterface(String iface) { |
| 195 | // If we don't already have an interface, and if this interface matches |
| 196 | // our regex, start tracking it. |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 197 | if (!iface.matches(mIfaceMatch) || isTrackingInterface()) |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 198 | return false; |
| 199 | |
| 200 | Log.d(TAG, "Started tracking interface " + iface); |
| 201 | setInterfaceUp(iface); |
| 202 | return true; |
| 203 | } |
| 204 | |
| 205 | private void stopTrackingInterface(String iface) { |
| 206 | if (!iface.equals(mIface)) |
| 207 | return; |
| 208 | |
| 209 | Log.d(TAG, "Stopped tracking interface " + iface); |
Lorenzo Colitti | 74d270d | 2014-10-01 13:52:40 +0900 | [diff] [blame] | 210 | // TODO: Unify this codepath with stop(). |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 211 | synchronized (this) { |
Lorenzo Colitti | 74d270d | 2014-10-01 13:52:40 +0900 | [diff] [blame] | 212 | NetworkUtils.stopDhcp(mIface); |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 213 | setInterfaceInfoLocked("", null); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 214 | mNetworkInfo.setExtraInfo(null); |
Lorenzo Colitti | 74d270d | 2014-10-01 13:52:40 +0900 | [diff] [blame] | 215 | mLinkUp = false; |
| 216 | mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); |
| 217 | updateAgent(); |
| 218 | mNetworkAgent = null; |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 219 | mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); |
| 220 | mLinkProperties = new LinkProperties(); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 221 | } |
| 222 | } |
| 223 | |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 224 | private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) { |
| 225 | if (staticConfig.ipAddress != null && |
| 226 | staticConfig.gateway != null && |
| 227 | staticConfig.dnsServers.size() > 0) { |
| 228 | try { |
| 229 | Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + staticConfig); |
| 230 | InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface); |
| 231 | config.setLinkAddress(staticConfig.ipAddress); |
| 232 | mNMService.setInterfaceConfig(mIface, config); |
| 233 | return true; |
| 234 | } catch(RemoteException|IllegalStateException e) { |
| 235 | Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 236 | } |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 237 | } else { |
| 238 | Log.e(TAG, "Invalid static IP configuration."); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 239 | } |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 240 | return false; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 241 | } |
| 242 | |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 243 | public void updateAgent() { |
| 244 | synchronized (EthernetNetworkFactory.this) { |
| 245 | if (mNetworkAgent == null) return; |
| 246 | if (DBG) { |
| 247 | Log.i(TAG, "Updating mNetworkAgent with: " + |
| 248 | mNetworkCapabilities + ", " + |
| 249 | mNetworkInfo + ", " + |
| 250 | mLinkProperties); |
| 251 | } |
| 252 | mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); |
| 253 | mNetworkAgent.sendNetworkInfo(mNetworkInfo); |
| 254 | mNetworkAgent.sendLinkProperties(mLinkProperties); |
Robert Greenwalt | e3cbf2e | 2014-09-10 10:06:32 -0700 | [diff] [blame] | 255 | // never set the network score below 0. |
| 256 | mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 257 | } |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 258 | } |
| 259 | |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 260 | /* Called by the NetworkFactory on the handler thread. */ |
| 261 | public void onRequestNetwork() { |
| 262 | // TODO: Handle DHCP renew. |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 263 | Thread dhcpThread = new Thread(new Runnable() { |
| 264 | public void run() { |
Lorenzo Colitti | 0e47a8a | 2015-08-04 13:27:28 +0900 | [diff] [blame] | 265 | if (DBG) Log.i(TAG, "dhcpThread(" + mIface + "): mNetworkInfo=" + mNetworkInfo); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 266 | LinkProperties linkProperties; |
| 267 | |
| 268 | IpConfiguration config = mEthernetManager.getConfiguration(); |
| 269 | |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 270 | if (config.getIpAssignment() == IpAssignment.STATIC) { |
| 271 | if (!setStaticIpAddress(config.getStaticIpConfiguration())) { |
| 272 | // We've already logged an error. |
| 273 | return; |
| 274 | } |
| 275 | linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 276 | } else { |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 277 | mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 278 | |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 279 | DhcpResults dhcpResults = new DhcpResults(); |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 280 | // TODO: Handle DHCP renewals better. |
| 281 | // In general runDhcp handles DHCP renewals for us, because |
| 282 | // the dhcp client stays running, but if the renewal fails, |
| 283 | // we will lose our IP address and connectivity without |
| 284 | // noticing. |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 285 | if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { |
| 286 | Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); |
Robert Greenwalt | e3cbf2e | 2014-09-10 10:06:32 -0700 | [diff] [blame] | 287 | // set our score lower than any network could go |
| 288 | // so we get dropped. |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 289 | mFactory.setScoreFilter(-1); |
Lorenzo Colitti | 0e47a8a | 2015-08-04 13:27:28 +0900 | [diff] [blame] | 290 | // If DHCP timed out (as opposed to failing), the DHCP client will still be |
| 291 | // running, because in M we changed its timeout to infinite. Stop it now. |
| 292 | NetworkUtils.stopDhcp(mIface); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 293 | return; |
| 294 | } |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 295 | linkProperties = dhcpResults.toLinkProperties(mIface); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 296 | } |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 297 | if (config.getProxySettings() == ProxySettings.STATIC || |
| 298 | config.getProxySettings() == ProxySettings.PAC) { |
| 299 | linkProperties.setHttpProxy(config.getHttpProxy()); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 300 | } |
| 301 | |
Robert Greenwalt | 69b9c6a | 2014-08-21 16:28:18 -0700 | [diff] [blame] | 302 | String tcpBufferSizes = mContext.getResources().getString( |
| 303 | com.android.internal.R.string.config_ethernet_tcp_buffers); |
| 304 | if (TextUtils.isEmpty(tcpBufferSizes) == false) { |
| 305 | linkProperties.setTcpBufferSizes(tcpBufferSizes); |
| 306 | } |
Robert Greenwalt | dcbf3b9 | 2014-08-06 22:27:17 -0700 | [diff] [blame] | 307 | |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 308 | synchronized(EthernetNetworkFactory.this) { |
Robert Greenwalt | 3191f90 | 2014-06-19 16:26:03 -0700 | [diff] [blame] | 309 | if (mNetworkAgent != null) { |
| 310 | Log.e(TAG, "Already have a NetworkAgent - aborting new request"); |
| 311 | return; |
| 312 | } |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 313 | mLinkProperties = linkProperties; |
| 314 | mNetworkInfo.setIsAvailable(true); |
| 315 | mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 316 | |
| 317 | // Create our NetworkAgent. |
| 318 | mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext, |
| 319 | NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties, |
| 320 | NETWORK_SCORE) { |
| 321 | public void unwanted() { |
Robert Greenwalt | 3191f90 | 2014-06-19 16:26:03 -0700 | [diff] [blame] | 322 | synchronized(EthernetNetworkFactory.this) { |
| 323 | if (this == mNetworkAgent) { |
| 324 | NetworkUtils.stopDhcp(mIface); |
| 325 | |
| 326 | mLinkProperties.clear(); |
| 327 | mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, |
| 328 | mHwAddr); |
| 329 | updateAgent(); |
| 330 | mNetworkAgent = null; |
| 331 | try { |
| 332 | mNMService.clearInterfaceAddresses(mIface); |
| 333 | } catch (Exception e) { |
| 334 | Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); |
| 335 | } |
| 336 | } else { |
| 337 | Log.d(TAG, "Ignoring unwanted as we have a more modern " + |
| 338 | "instance"); |
| 339 | } |
| 340 | } |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 341 | }; |
| 342 | }; |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 343 | } |
| 344 | } |
| 345 | }); |
| 346 | dhcpThread.start(); |
| 347 | } |
| 348 | |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 349 | /** |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 350 | * Begin monitoring connectivity |
| 351 | */ |
| 352 | public synchronized void start(Context context, Handler target) { |
| 353 | // The services we use. |
| 354 | IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); |
| 355 | mNMService = INetworkManagementService.Stub.asInterface(b); |
| 356 | mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); |
| 357 | |
| 358 | // Interface match regex. |
| 359 | mIfaceMatch = context.getResources().getString( |
| 360 | com.android.internal.R.string.config_ethernet_iface_regex); |
| 361 | |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 362 | // Create and register our NetworkFactory. |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 363 | mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, target.getLooper()); |
| 364 | mFactory.setCapabilityFilter(mNetworkCapabilities); |
| 365 | mFactory.setScoreFilter(-1); // this set high when we have an iface |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 366 | mFactory.register(); |
| 367 | |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 368 | mContext = context; |
| 369 | |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 370 | // Start tracking interface change events. |
| 371 | mInterfaceObserver = new InterfaceObserver(); |
| 372 | try { |
| 373 | mNMService.registerObserver(mInterfaceObserver); |
| 374 | } catch (RemoteException e) { |
| 375 | Log.e(TAG, "Could not register InterfaceObserver " + e); |
| 376 | } |
| 377 | |
| 378 | // If an Ethernet interface is already connected, start tracking that. |
| 379 | // Otherwise, the first Ethernet interface to appear will be tracked. |
| 380 | try { |
| 381 | final String[] ifaces = mNMService.listInterfaces(); |
| 382 | for (String iface : ifaces) { |
Lorenzo Colitti | 044a758 | 2014-05-22 12:26:37 -0700 | [diff] [blame] | 383 | synchronized(this) { |
| 384 | if (maybeTrackInterface(iface)) { |
| 385 | // We have our interface. Track it. |
| 386 | // Note: if the interface already has link (e.g., if we |
| 387 | // crashed and got restarted while it was running), |
| 388 | // we need to fake a link up notification so we start |
| 389 | // configuring it. Since we're already holding the lock, |
| 390 | // any real link up/down notification will only arrive |
| 391 | // after we've done this. |
| 392 | if (mNMService.getInterfaceConfig(iface).hasFlag("running")) { |
| 393 | updateInterfaceState(iface, true); |
| 394 | } |
| 395 | break; |
| 396 | } |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 397 | } |
| 398 | } |
Lorenzo Colitti | 9fc9da2 | 2015-09-08 16:21:52 +0900 | [diff] [blame] | 399 | } catch (RemoteException|IllegalStateException e) { |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 400 | Log.e(TAG, "Could not get list of interfaces " + e); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | public synchronized void stop() { |
Lorenzo Colitti | 41a372f | 2014-08-12 10:49:39 +0900 | [diff] [blame] | 405 | NetworkUtils.stopDhcp(mIface); |
| 406 | // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object |
| 407 | // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here: |
| 408 | // that sets the state to IDLE, and ConnectivityService will still think we're connected. |
| 409 | // |
| 410 | // TODO: stop using explicit comparisons to DISCONNECTED / SUSPENDED in ConnectivityService, |
| 411 | // and instead use isConnectedOrConnecting(). |
| 412 | mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); |
| 413 | mLinkUp = false; |
| 414 | updateAgent(); |
| 415 | mLinkProperties = new LinkProperties(); |
| 416 | mNetworkAgent = null; |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 417 | setInterfaceInfoLocked("", null); |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 418 | mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); |
Robert Greenwalt | 0d1b174 | 2014-05-27 17:59:24 -0700 | [diff] [blame] | 419 | mFactory.unregister(); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 420 | } |
| 421 | |
| 422 | private void initNetworkCapabilities() { |
| 423 | mNetworkCapabilities = new NetworkCapabilities(); |
| 424 | mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); |
Robert Greenwalt | 4b1ec3f | 2014-06-09 17:36:54 -0700 | [diff] [blame] | 425 | mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); |
| 426 | mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 427 | // We have no useful data on bandwidth. Say 100M up and 100M down. :-( |
| 428 | mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000); |
| 429 | mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000); |
| 430 | } |
Lorenzo Colitti | aea43f5 | 2014-08-19 11:45:46 -0700 | [diff] [blame] | 431 | |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 432 | public synchronized boolean isTrackingInterface() { |
| 433 | return !TextUtils.isEmpty(mIface); |
| 434 | } |
| 435 | |
| 436 | /** |
| 437 | * Set interface information and notify listeners if availability is changed. |
| 438 | * This should be called with the lock held. |
| 439 | */ |
| 440 | private void setInterfaceInfoLocked(String iface, String hwAddr) { |
| 441 | boolean oldAvailable = isTrackingInterface(); |
| 442 | mIface = iface; |
| 443 | mHwAddr = hwAddr; |
| 444 | boolean available = isTrackingInterface(); |
| 445 | |
| 446 | if (oldAvailable != available) { |
| 447 | int n = mListeners.beginBroadcast(); |
| 448 | for (int i = 0; i < n; i++) { |
| 449 | try { |
| 450 | mListeners.getBroadcastItem(i).onAvailabilityChanged(available); |
| 451 | } catch (RemoteException e) { |
| 452 | // Do nothing here. |
| 453 | } |
| 454 | } |
| 455 | mListeners.finishBroadcast(); |
| 456 | } |
| 457 | } |
| 458 | |
Lorenzo Colitti | aea43f5 | 2014-08-19 11:45:46 -0700 | [diff] [blame] | 459 | synchronized void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { |
Jaewan Kim | a3d7e61 | 2014-10-20 12:03:46 +0900 | [diff] [blame] | 460 | if (isTrackingInterface()) { |
Lorenzo Colitti | aea43f5 | 2014-08-19 11:45:46 -0700 | [diff] [blame] | 461 | pw.println("Tracking interface: " + mIface); |
| 462 | pw.increaseIndent(); |
| 463 | pw.println("MAC address: " + mHwAddr); |
| 464 | pw.println("Link state: " + (mLinkUp ? "up" : "down")); |
| 465 | pw.decreaseIndent(); |
| 466 | } else { |
| 467 | pw.println("Not tracking any interface"); |
| 468 | } |
| 469 | |
| 470 | pw.println(); |
| 471 | pw.println("NetworkInfo: " + mNetworkInfo); |
| 472 | pw.println("LinkProperties: " + mLinkProperties); |
| 473 | pw.println("NetworkAgent: " + mNetworkAgent); |
| 474 | } |
Lorenzo Colitti | 20c1c99 | 2014-05-20 16:58:34 -0700 | [diff] [blame] | 475 | } |