NetworkManagementService.java revision ce1200d42c46ae5d3ec637587b07dfdc02ad21c0
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import android.app.PendingIntent; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.res.Resources; 25import android.content.pm.PackageManager; 26import android.net.Uri; 27import android.net.InterfaceConfiguration; 28import android.net.INetworkManagementEventObserver; 29import android.os.INetworkManagementService; 30import android.os.Handler; 31import android.os.SystemProperties; 32import android.text.TextUtils; 33import android.util.Log; 34import java.util.ArrayList; 35import java.util.StringTokenizer; 36import android.provider.Settings; 37import android.content.ContentResolver; 38import android.database.ContentObserver; 39 40import java.io.File; 41import java.io.FileReader; 42import java.lang.IllegalStateException; 43 44import java.net.InetAddress; 45import java.net.UnknownHostException; 46 47/** 48 * @hide 49 */ 50class NetworkManagementService extends INetworkManagementService.Stub { 51 52 private static final String TAG = "NetworkManagmentService"; 53 54 class NetdResponseCode { 55 public static final int InterfaceListResult = 110; 56 public static final int TetherInterfaceListResult = 111; 57 public static final int TetherDnsFwdTgtListResult = 112; 58 public static final int TtyListResult = 113; 59 60 public static final int TetherStatusResult = 210; 61 public static final int IpFwdStatusResult = 211; 62 public static final int InterfaceGetCfgResult = 213; 63 public static final int SoftapStatusResult = 214; 64 public static final int UsbRNDISStatusResult = 215; 65 66 public static final int InterfaceChange = 600; 67 } 68 69 /** 70 * Binder context for this service 71 */ 72 private Context mContext; 73 74 /** 75 * connector object for communicating with netd 76 */ 77 private NativeDaemonConnector mConnector; 78 79 private ArrayList<INetworkManagementEventObserver> mObservers; 80 81 /** 82 * Constructs a new NetworkManagementService instance 83 * 84 * @param context Binder context for this service 85 */ 86 public NetworkManagementService(Context context) { 87 mContext = context; 88 89 mObservers = new ArrayList<INetworkManagementEventObserver>(); 90 91 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 92 return; 93 } 94 95 mConnector = new NativeDaemonConnector( 96 new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); 97 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); 98 thread.start(); 99 } 100 101 public void registerObserver(INetworkManagementEventObserver obs) { 102 Log.d(TAG, "Registering observer"); 103 mObservers.add(obs); 104 } 105 106 public void unregisterObserver(INetworkManagementEventObserver obs) { 107 Log.d(TAG, "Unregistering observer"); 108 mObservers.remove(mObservers.indexOf(obs)); 109 } 110 111 /** 112 * Notify our observers of an interface link status change 113 */ 114 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { 115 for (INetworkManagementEventObserver obs : mObservers) { 116 try { 117 obs.interfaceLinkStatusChanged(iface, link); 118 } catch (Exception ex) { 119 Log.w(TAG, "Observer notifier failed", ex); 120 } 121 } 122 } 123 124 /** 125 * Notify our observers of an interface addition. 126 */ 127 private void notifyInterfaceAdded(String iface) { 128 for (INetworkManagementEventObserver obs : mObservers) { 129 try { 130 obs.interfaceAdded(iface); 131 } catch (Exception ex) { 132 Log.w(TAG, "Observer notifier failed", ex); 133 } 134 } 135 } 136 137 /** 138 * Notify our observers of an interface removal. 139 */ 140 private void notifyInterfaceRemoved(String iface) { 141 for (INetworkManagementEventObserver obs : mObservers) { 142 try { 143 obs.interfaceRemoved(iface); 144 } catch (Exception ex) { 145 Log.w(TAG, "Observer notifier failed", ex); 146 } 147 } 148 } 149 150 151 // 152 // Netd Callback handling 153 // 154 155 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 156 public void onDaemonConnected() { 157 new Thread() { 158 public void run() { 159 } 160 }.start(); 161 } 162 public boolean onEvent(int code, String raw, String[] cooked) { 163 if (code == NetdResponseCode.InterfaceChange) { 164 /* 165 * a network interface change occured 166 * Format: "NNN Iface added <name>" 167 * "NNN Iface removed <name>" 168 * "NNN Iface changed <name> <up/down>" 169 */ 170 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 171 throw new IllegalStateException( 172 String.format("Invalid event from daemon (%s)", raw)); 173 } 174 if (cooked[2].equals("added")) { 175 notifyInterfaceAdded(cooked[3]); 176 return true; 177 } else if (cooked[2].equals("removed")) { 178 notifyInterfaceRemoved(cooked[3]); 179 return true; 180 } else if (cooked[2].equals("changed") && cooked.length == 5) { 181 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); 182 return true; 183 } 184 throw new IllegalStateException( 185 String.format("Invalid event from daemon (%s)", raw)); 186 } 187 return false; 188 } 189 } 190 191 private static int stringToIpAddr(String addrString) throws UnknownHostException { 192 try { 193 String[] parts = addrString.split("\\."); 194 if (parts.length != 4) { 195 throw new UnknownHostException(addrString); 196 } 197 198 int a = Integer.parseInt(parts[0]) ; 199 int b = Integer.parseInt(parts[1]) << 8; 200 int c = Integer.parseInt(parts[2]) << 16; 201 int d = Integer.parseInt(parts[3]) << 24; 202 203 return a | b | c | d; 204 } catch (NumberFormatException ex) { 205 throw new UnknownHostException(addrString); 206 } 207 } 208 209 public static String intToIpString(int i) { 210 return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." + 211 (i & 0xFF); 212 } 213 214 // 215 // INetworkManagementService members 216 // 217 218 public String[] listInterfaces() throws IllegalStateException { 219 mContext.enforceCallingOrSelfPermission( 220 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 221 222 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); 223 } 224 225 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { 226 String rsp = mConnector.doCommand("interface getcfg " + iface).get(0); 227 Log.d(TAG, String.format("rsp <%s>", rsp)); 228 229 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] 230 StringTokenizer st = new StringTokenizer(rsp); 231 232 try { 233 int code = Integer.parseInt(st.nextToken(" ")); 234 if (code != NetdResponseCode.InterfaceGetCfgResult) { 235 throw new IllegalStateException( 236 String.format("Expected code %d, but got %d", 237 NetdResponseCode.InterfaceGetCfgResult, code)); 238 } 239 } catch (NumberFormatException nfe) { 240 throw new IllegalStateException( 241 String.format("Invalid response from daemon (%s)", rsp)); 242 } 243 244 InterfaceConfiguration cfg = new InterfaceConfiguration(); 245 cfg.hwAddr = st.nextToken(" "); 246 try { 247 cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); 248 } catch (UnknownHostException uhe) { 249 Log.e(TAG, "Failed to parse ipaddr", uhe); 250 cfg.ipAddr = 0; 251 } 252 253 try { 254 cfg.netmask = stringToIpAddr(st.nextToken(" ")); 255 } catch (UnknownHostException uhe) { 256 Log.e(TAG, "Failed to parse netmask", uhe); 257 cfg.netmask = 0; 258 } 259 cfg.interfaceFlags = st.nextToken("]"); 260 Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 261 return cfg; 262 } 263 264 public void setInterfaceConfig( 265 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 266 String cmd = String.format("interface setcfg %s %s %s", iface, 267 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); 268 mConnector.doCommand(cmd); 269 } 270 271 public void shutdown() { 272 if (mContext.checkCallingOrSelfPermission( 273 android.Manifest.permission.SHUTDOWN) 274 != PackageManager.PERMISSION_GRANTED) { 275 throw new SecurityException("Requires SHUTDOWN permission"); 276 } 277 278 Log.d(TAG, "Shutting down"); 279 } 280 281 public boolean getIpForwardingEnabled() throws IllegalStateException{ 282 mContext.enforceCallingOrSelfPermission( 283 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 284 285 ArrayList<String> rsp = mConnector.doCommand("ipfwd status"); 286 287 for (String line : rsp) { 288 String []tok = line.split(" "); 289 int code = Integer.parseInt(tok[0]); 290 if (code == NetdResponseCode.IpFwdStatusResult) { 291 // 211 Forwarding <enabled/disabled> 292 if (tok.length !=2) { 293 throw new IllegalStateException( 294 String.format("Malformatted list entry '%s'", line)); 295 } 296 if (tok[2].equals("enabled")) 297 return true; 298 return false; 299 } else { 300 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 301 } 302 } 303 throw new IllegalStateException("Got an empty response"); 304 } 305 306 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { 307 mContext.enforceCallingOrSelfPermission( 308 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 309 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); 310 } 311 312 public void startTethering(String dhcpRangeStart, String dhcpRangeEnd) 313 throws IllegalStateException { 314 mContext.enforceCallingOrSelfPermission( 315 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 316 mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd)); 317 } 318 319 public void stopTethering() throws IllegalStateException { 320 mContext.enforceCallingOrSelfPermission( 321 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 322 mConnector.doCommand("tether stop"); 323 } 324 325 public boolean isTetheringStarted() throws IllegalStateException { 326 mContext.enforceCallingOrSelfPermission( 327 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 328 329 ArrayList<String> rsp = mConnector.doCommand("tether status"); 330 331 for (String line : rsp) { 332 String []tok = line.split(" "); 333 int code = Integer.parseInt(tok[0]); 334 if (code == NetdResponseCode.TetherStatusResult) { 335 // XXX: Tethering services <started/stopped> <TBD>... 336 if (tok[2].equals("started")) 337 return true; 338 return false; 339 } else { 340 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 341 } 342 } 343 throw new IllegalStateException("Got an empty response"); 344 } 345 346 public void tetherInterface(String iface) throws IllegalStateException { 347 mContext.enforceCallingOrSelfPermission( 348 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 349 mConnector.doCommand("tether interface add " + iface); 350 } 351 352 public void untetherInterface(String iface) { 353 mContext.enforceCallingOrSelfPermission( 354 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 355 mConnector.doCommand("tether interface remove " + iface); 356 } 357 358 public String[] listTetheredInterfaces() throws IllegalStateException { 359 mContext.enforceCallingOrSelfPermission( 360 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 361 return mConnector.doListCommand( 362 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 363 } 364 365 public void setDnsForwarders(String[] dns) throws IllegalStateException { 366 mContext.enforceCallingOrSelfPermission( 367 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 368 try { 369 String cmd = "tether dns set"; 370 for (String s : dns) { 371 cmd += " " + InetAddress.getByName(s).getHostAddress(); 372 } 373 mConnector.doCommand(cmd); 374 } catch (UnknownHostException e) { 375 throw new IllegalStateException("Error resolving dns name", e); 376 } 377 } 378 379 public String[] getDnsForwarders() throws IllegalStateException { 380 mContext.enforceCallingOrSelfPermission( 381 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 382 return mConnector.doListCommand( 383 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 384 } 385 386 public void enableNat(String internalInterface, String externalInterface) 387 throws IllegalStateException { 388 mContext.enforceCallingOrSelfPermission( 389 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 390 mConnector.doCommand( 391 String.format("nat enable %s %s", internalInterface, externalInterface)); 392 } 393 394 public void disableNat(String internalInterface, String externalInterface) 395 throws IllegalStateException { 396 mContext.enforceCallingOrSelfPermission( 397 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 398 mConnector.doCommand( 399 String.format("nat disable %s %s", internalInterface, externalInterface)); 400 } 401 402 public String[] listTtys() throws IllegalStateException { 403 mContext.enforceCallingOrSelfPermission( 404 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 405 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); 406 } 407 408 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 409 String dns2Addr) throws IllegalStateException { 410 try { 411 mContext.enforceCallingOrSelfPermission( 412 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 413 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, 414 InetAddress.getByName(localAddr).getHostAddress(), 415 InetAddress.getByName(remoteAddr).getHostAddress(), 416 InetAddress.getByName(dns1Addr).getHostAddress(), 417 InetAddress.getByName(dns2Addr).getHostAddress())); 418 } catch (UnknownHostException e) { 419 throw new IllegalStateException("Error resolving addr", e); 420 } 421 } 422 423 public void detachPppd(String tty) throws IllegalStateException { 424 mContext.enforceCallingOrSelfPermission( 425 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 426 mConnector.doCommand(String.format("pppd detach %s", tty)); 427 } 428 429 public void startUsbRNDIS() throws IllegalStateException { 430 mContext.enforceCallingOrSelfPermission( 431 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 432 mConnector.doCommand("usb startrndis"); 433 } 434 435 public void stopUsbRNDIS() throws IllegalStateException { 436 mContext.enforceCallingOrSelfPermission( 437 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 438 mConnector.doCommand("usb stoprndis"); 439 } 440 441 public boolean isUsbRNDISStarted() throws IllegalStateException { 442 mContext.enforceCallingOrSelfPermission( 443 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 444 ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus"); 445 446 for (String line : rsp) { 447 String []tok = line.split(" "); 448 int code = Integer.parseInt(tok[0]); 449 if (code == NetdResponseCode.UsbRNDISStatusResult) { 450 if (tok[2].equals("started")) 451 return true; 452 return false; 453 } else { 454 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 455 } 456 } 457 throw new IllegalStateException("Got an empty response"); 458 } 459} 460