blob: 634a3f207cdd15d79bd8d0973e17c7ae1a89e3d1 [file] [log] [blame]
San Mehat873f2142010-01-14 10:25:07 -08001/*
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
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070019import static android.net.NetworkStats.IFACE_ALL;
20import static android.net.NetworkStats.TAG_NONE;
21import static android.net.NetworkStats.UID_ALL;
Jeff Sharkey0a9ee122011-06-22 16:32:41 -070022import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070023
San Mehat873f2142010-01-14 10:25:07 -080024import android.content.Context;
San Mehat873f2142010-01-14 10:25:07 -080025import android.content.pm.PackageManager;
San Mehat4d02d002010-01-22 16:07:46 -080026import android.net.INetworkManagementEventObserver;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070027import android.net.InterfaceConfiguration;
Robert Greenwalted126402011-01-28 15:34:55 -080028import android.net.LinkAddress;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070029import android.net.NetworkStats;
Robert Greenwalted126402011-01-28 15:34:55 -080030import android.net.NetworkUtils;
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070031import android.net.RouteInfo;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080032import android.net.wifi.WifiConfiguration;
33import android.net.wifi.WifiConfiguration.KeyMgmt;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070034import android.os.Binder;
San Mehat873f2142010-01-14 10:25:07 -080035import android.os.INetworkManagementService;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070036import android.os.SystemClock;
Marco Nelissen62dbb222010-02-18 10:56:30 -080037import android.os.SystemProperties;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080038import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080039import android.util.Slog;
San Mehat873f2142010-01-14 10:25:07 -080040
Jeff Sharkey4414cea2011-06-24 17:05:24 -070041import com.google.android.collect.Lists;
42import com.google.android.collect.Maps;
43
44import dalvik.system.BlockGuard;
45
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070046import java.io.BufferedReader;
47import java.io.DataInputStream;
San Mehat873f2142010-01-14 10:25:07 -080048import java.io.File;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070049import java.io.FileInputStream;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070050import java.io.FileReader;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070051import java.io.IOException;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070052import java.io.InputStreamReader;
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070053import java.net.Inet4Address;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070054import java.net.InetAddress;
55import java.util.ArrayList;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070056import java.util.HashMap;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070057import java.util.NoSuchElementException;
58import java.util.StringTokenizer;
Robert Greenwalte5c3afb2010-09-22 14:32:35 -070059import java.util.concurrent.CountDownLatch;
San Mehat873f2142010-01-14 10:25:07 -080060
Jeff Sharkey9a13f362011-04-26 16:25:36 -070061import libcore.io.IoUtils;
62
San Mehat873f2142010-01-14 10:25:07 -080063/**
64 * @hide
65 */
66class NetworkManagementService extends INetworkManagementService.Stub {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070067 private static final String TAG = "NetworkManagementService";
Dianne Hackborncef65ee2010-09-30 18:27:22 -070068 private static final boolean DBG = false;
Kenny Root305bcbf2010-09-03 07:56:38 -070069 private static final String NETD_TAG = "NetdConnector";
70
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070071 private static final int ADD = 1;
72 private static final int REMOVE = 2;
73
Jeff Sharkey4414cea2011-06-24 17:05:24 -070074 /** Path to {@code /proc/uid_stat}. */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070075 @Deprecated
Jeff Sharkey4414cea2011-06-24 17:05:24 -070076 private final File mProcStatsUidstat;
77 /** Path to {@code /proc/net/xt_qtaguid/stats}. */
78 private final File mProcStatsNetfilter;
79
80 /** {@link #mProcStatsNetfilter} headers. */
81 private static final String KEY_IFACE = "iface";
82 private static final String KEY_TAG_HEX = "acct_tag_hex";
83 private static final String KEY_UID = "uid_tag_int";
84 private static final String KEY_RX = "rx_bytes";
85 private static final String KEY_TX = "tx_bytes";
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070086
San Mehat873f2142010-01-14 10:25:07 -080087 class NetdResponseCode {
88 public static final int InterfaceListResult = 110;
89 public static final int TetherInterfaceListResult = 111;
90 public static final int TetherDnsFwdTgtListResult = 112;
San Mehat72759df2010-01-19 13:50:37 -080091 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080092
93 public static final int TetherStatusResult = 210;
94 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080095 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080096 public static final int SoftapStatusResult = 214;
San Mehat91cac642010-03-31 14:31:36 -070097 public static final int InterfaceRxCounterResult = 216;
98 public static final int InterfaceTxCounterResult = 217;
99 public static final int InterfaceRxThrottleResult = 218;
100 public static final int InterfaceTxThrottleResult = 219;
Robert Greenwalte3253922010-02-18 09:23:25 -0800101
102 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -0800103 }
104
105 /**
106 * Binder context for this service
107 */
108 private Context mContext;
109
110 /**
111 * connector object for communicating with netd
112 */
113 private NativeDaemonConnector mConnector;
114
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700115 private Thread mThread;
116 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
117
San Mehat4d02d002010-01-22 16:07:46 -0800118 private ArrayList<INetworkManagementEventObserver> mObservers;
119
San Mehat873f2142010-01-14 10:25:07 -0800120 /**
121 * Constructs a new NetworkManagementService instance
122 *
123 * @param context Binder context for this service
124 */
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700125 private NetworkManagementService(Context context, File procRoot) {
San Mehat873f2142010-01-14 10:25:07 -0800126 mContext = context;
San Mehat4d02d002010-01-22 16:07:46 -0800127 mObservers = new ArrayList<INetworkManagementEventObserver>();
128
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700129 mProcStatsUidstat = new File(procRoot, "uid_stat");
130 mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
131
Marco Nelissen62dbb222010-02-18 10:56:30 -0800132 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
133 return;
134 }
135
San Mehat873f2142010-01-14 10:25:07 -0800136 mConnector = new NativeDaemonConnector(
Kenny Root305bcbf2010-09-03 07:56:38 -0700137 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700138 mThread = new Thread(mConnector, NETD_TAG);
139 }
140
141 public static NetworkManagementService create(Context context) throws InterruptedException {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700142 NetworkManagementService service = new NetworkManagementService(
143 context, new File("/proc/"));
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700144 if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
145 service.mThread.start();
146 if (DBG) Slog.d(TAG, "Awaiting socket connection");
147 service.mConnectedSignal.await();
148 if (DBG) Slog.d(TAG, "Connected");
149 return service;
San Mehat873f2142010-01-14 10:25:07 -0800150 }
151
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700152 // @VisibleForTesting
153 public static NetworkManagementService createForTest(Context context, File procRoot) {
154 // TODO: eventually connect with mock netd
155 return new NetworkManagementService(context, procRoot);
156 }
157
San Mehat4d02d002010-01-22 16:07:46 -0800158 public void registerObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800159 Slog.d(TAG, "Registering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800160 mObservers.add(obs);
161 }
162
163 public void unregisterObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800164 Slog.d(TAG, "Unregistering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800165 mObservers.remove(mObservers.indexOf(obs));
166 }
167
168 /**
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700169 * Notify our observers of an interface status change
San Mehat4d02d002010-01-22 16:07:46 -0800170 */
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700171 private void notifyInterfaceStatusChanged(String iface, boolean up) {
San Mehat4d02d002010-01-22 16:07:46 -0800172 for (INetworkManagementEventObserver obs : mObservers) {
173 try {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700174 obs.interfaceStatusChanged(iface, up);
175 } catch (Exception ex) {
176 Slog.w(TAG, "Observer notifier failed", ex);
177 }
178 }
179 }
180
181 /**
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700182 * Notify our observers of an interface link state change
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700183 * (typically, an Ethernet cable has been plugged-in or unplugged).
184 */
185 private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
186 for (INetworkManagementEventObserver obs : mObservers) {
187 try {
188 obs.interfaceLinkStateChanged(iface, up);
San Mehat4d02d002010-01-22 16:07:46 -0800189 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800190 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800191 }
192 }
193 }
194
195 /**
196 * Notify our observers of an interface addition.
197 */
198 private void notifyInterfaceAdded(String iface) {
199 for (INetworkManagementEventObserver obs : mObservers) {
200 try {
201 obs.interfaceAdded(iface);
202 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800203 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800204 }
205 }
206 }
207
208 /**
209 * Notify our observers of an interface removal.
210 */
211 private void notifyInterfaceRemoved(String iface) {
212 for (INetworkManagementEventObserver obs : mObservers) {
213 try {
214 obs.interfaceRemoved(iface);
215 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800216 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800217 }
218 }
219 }
220
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700221 /**
222 * Let us know the daemon is connected
223 */
224 protected void onConnected() {
225 if (DBG) Slog.d(TAG, "onConnected");
226 mConnectedSignal.countDown();
227 }
228
San Mehat4d02d002010-01-22 16:07:46 -0800229
San Mehat873f2142010-01-14 10:25:07 -0800230 //
231 // Netd Callback handling
232 //
233
234 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
235 public void onDaemonConnected() {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700236 NetworkManagementService.this.onConnected();
San Mehat873f2142010-01-14 10:25:07 -0800237 new Thread() {
238 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800239 }
240 }.start();
241 }
242 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800243 if (code == NetdResponseCode.InterfaceChange) {
244 /*
245 * a network interface change occured
246 * Format: "NNN Iface added <name>"
247 * "NNN Iface removed <name>"
248 * "NNN Iface changed <name> <up/down>"
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700249 * "NNN Iface linkstatus <name> <up/down>"
Robert Greenwalte3253922010-02-18 09:23:25 -0800250 */
251 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
252 throw new IllegalStateException(
253 String.format("Invalid event from daemon (%s)", raw));
254 }
255 if (cooked[2].equals("added")) {
256 notifyInterfaceAdded(cooked[3]);
257 return true;
258 } else if (cooked[2].equals("removed")) {
259 notifyInterfaceRemoved(cooked[3]);
260 return true;
261 } else if (cooked[2].equals("changed") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700262 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
263 return true;
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700264 } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700265 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
Robert Greenwalte3253922010-02-18 09:23:25 -0800266 return true;
267 }
268 throw new IllegalStateException(
269 String.format("Invalid event from daemon (%s)", raw));
270 }
271 return false;
San Mehat873f2142010-01-14 10:25:07 -0800272 }
273 }
274
San Mehated4fc8a2010-01-22 12:28:36 -0800275
San Mehat873f2142010-01-14 10:25:07 -0800276 //
277 // INetworkManagementService members
278 //
279
280 public String[] listInterfaces() throws IllegalStateException {
281 mContext.enforceCallingOrSelfPermission(
282 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
283
Kenny Roota80ce062010-06-01 13:23:53 -0700284 try {
285 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
286 } catch (NativeDaemonConnectorException e) {
287 throw new IllegalStateException(
288 "Cannot communicate with native daemon to list interfaces");
289 }
San Mehated4fc8a2010-01-22 12:28:36 -0800290 }
291
292 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
Kenny Roota80ce062010-06-01 13:23:53 -0700293 String rsp;
294 try {
295 rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
296 } catch (NativeDaemonConnectorException e) {
297 throw new IllegalStateException(
298 "Cannot communicate with native daemon to get interface config");
299 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800300 Slog.d(TAG, String.format("rsp <%s>", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800301
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800302 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
San Mehated4fc8a2010-01-22 12:28:36 -0800303 StringTokenizer st = new StringTokenizer(rsp);
304
Kenny Roota80ce062010-06-01 13:23:53 -0700305 InterfaceConfiguration cfg;
San Mehated4fc8a2010-01-22 12:28:36 -0800306 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700307 try {
308 int code = Integer.parseInt(st.nextToken(" "));
309 if (code != NetdResponseCode.InterfaceGetCfgResult) {
310 throw new IllegalStateException(
311 String.format("Expected code %d, but got %d",
312 NetdResponseCode.InterfaceGetCfgResult, code));
313 }
314 } catch (NumberFormatException nfe) {
San Mehated4fc8a2010-01-22 12:28:36 -0800315 throw new IllegalStateException(
Kenny Roota80ce062010-06-01 13:23:53 -0700316 String.format("Invalid response from daemon (%s)", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800317 }
Kenny Roota80ce062010-06-01 13:23:53 -0700318
319 cfg = new InterfaceConfiguration();
320 cfg.hwAddr = st.nextToken(" ");
Robert Greenwalted126402011-01-28 15:34:55 -0800321 InetAddress addr = null;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800322 int prefixLength = 0;
Kenny Roota80ce062010-06-01 13:23:53 -0700323 try {
Robert Greenwalte5903732011-02-22 16:00:42 -0800324 addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
325 } catch (IllegalArgumentException iae) {
326 Slog.e(TAG, "Failed to parse ipaddr", iae);
Kenny Roota80ce062010-06-01 13:23:53 -0700327 }
328
329 try {
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800330 prefixLength = Integer.parseInt(st.nextToken(" "));
331 } catch (NumberFormatException nfe) {
332 Slog.e(TAG, "Failed to parse prefixLength", nfe);
Kenny Roota80ce062010-06-01 13:23:53 -0700333 }
Robert Greenwalt04808c22010-12-13 17:01:41 -0800334
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800335 cfg.addr = new LinkAddress(addr, prefixLength);
Kenny Roota80ce062010-06-01 13:23:53 -0700336 cfg.interfaceFlags = st.nextToken("]").trim() +"]";
337 } catch (NoSuchElementException nsee) {
San Mehated4fc8a2010-01-22 12:28:36 -0800338 throw new IllegalStateException(
339 String.format("Invalid response from daemon (%s)", rsp));
340 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800341 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
San Mehated4fc8a2010-01-22 12:28:36 -0800342 return cfg;
343 }
344
345 public void setInterfaceConfig(
346 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
Robert Greenwalted126402011-01-28 15:34:55 -0800347 LinkAddress linkAddr = cfg.addr;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800348 if (linkAddr == null || linkAddr.getAddress() == null) {
349 throw new IllegalStateException("Null LinkAddress given");
Robert Greenwalted126402011-01-28 15:34:55 -0800350 }
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800351 String cmd = String.format("interface setcfg %s %s %d %s", iface,
352 linkAddr.getAddress().getHostAddress(),
353 linkAddr.getNetworkPrefixLength(),
354 cfg.interfaceFlags);
Kenny Roota80ce062010-06-01 13:23:53 -0700355 try {
356 mConnector.doCommand(cmd);
357 } catch (NativeDaemonConnectorException e) {
358 throw new IllegalStateException(
Robert Greenwalt81d5ad52010-12-16 11:27:38 -0800359 "Unable to communicate with native daemon to interface setcfg - " + e);
Kenny Roota80ce062010-06-01 13:23:53 -0700360 }
San Mehat873f2142010-01-14 10:25:07 -0800361 }
362
Irfan Sherifff5600612011-06-16 10:26:28 -0700363 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
364 IPv6 addresses on interface down, but we need to do full clean up here */
365 public void clearInterfaceAddresses(String iface) throws IllegalStateException {
366 String cmd = String.format("interface clearaddrs %s", iface);
367 try {
368 mConnector.doCommand(cmd);
369 } catch (NativeDaemonConnectorException e) {
370 throw new IllegalStateException(
371 "Unable to communicate with native daemon to interface clearallips - " + e);
372 }
373 }
374
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -0700375 public void addRoute(String interfaceName, RouteInfo route) {
376 modifyRoute(interfaceName, ADD, route);
377 }
378
379 public void removeRoute(String interfaceName, RouteInfo route) {
380 modifyRoute(interfaceName, REMOVE, route);
381 }
382
383 private void modifyRoute(String interfaceName, int action, RouteInfo route) {
384 ArrayList<String> rsp;
385
386 StringBuilder cmd;
387
388 switch (action) {
389 case ADD:
390 {
391 cmd = new StringBuilder("interface route add " + interfaceName);
392 break;
393 }
394 case REMOVE:
395 {
396 cmd = new StringBuilder("interface route remove " + interfaceName);
397 break;
398 }
399 default:
400 throw new IllegalStateException("Unknown action type " + action);
401 }
402
403 // create triplet: dest-ip-addr prefixlength gateway-ip-addr
404 LinkAddress la = route.getDestination();
405 cmd.append(' ');
406 cmd.append(la.getAddress().getHostAddress());
407 cmd.append(' ');
408 cmd.append(la.getNetworkPrefixLength());
409 cmd.append(' ');
410 if (route.getGateway() == null) {
411 if (la.getAddress() instanceof Inet4Address) {
412 cmd.append("0.0.0.0");
413 } else {
414 cmd.append ("::0");
415 }
416 } else {
417 cmd.append(route.getGateway().getHostAddress());
418 }
419 try {
420 rsp = mConnector.doCommand(cmd.toString());
421 } catch (NativeDaemonConnectorException e) {
422 throw new IllegalStateException(
423 "Unable to communicate with native dameon to add routes - "
424 + e);
425 }
426
427 for (String line : rsp) {
428 Log.v(TAG, "add route response is " + line);
429 }
430 }
431
432 private ArrayList<String> readRouteList(String filename) {
433 FileInputStream fstream = null;
434 ArrayList<String> list = new ArrayList<String>();
435
436 try {
437 fstream = new FileInputStream(filename);
438 DataInputStream in = new DataInputStream(fstream);
439 BufferedReader br = new BufferedReader(new InputStreamReader(in));
440 String s;
441
442 // throw away the title line
443
444 while (((s = br.readLine()) != null) && (s.length() != 0)) {
445 list.add(s);
446 }
447 } catch (IOException ex) {
448 // return current list, possibly empty
449 } finally {
450 if (fstream != null) {
451 try {
452 fstream.close();
453 } catch (IOException ex) {}
454 }
455 }
456
457 return list;
458 }
459
460 public RouteInfo[] getRoutes(String interfaceName) {
461 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
462
463 // v4 routes listed as:
464 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
465 for (String s : readRouteList("/proc/net/route")) {
466 String[] fields = s.split("\t");
467
468 if (fields.length > 7) {
469 String iface = fields[0];
470
471 if (interfaceName.equals(iface)) {
472 String dest = fields[1];
473 String gate = fields[2];
474 String flags = fields[3]; // future use?
475 String mask = fields[7];
476 try {
477 // address stored as a hex string, ex: 0014A8C0
478 InetAddress destAddr =
479 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
480 int prefixLength =
481 NetworkUtils.netmaskIntToPrefixLength(
482 (int)Long.parseLong(mask, 16));
483 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
484
485 // address stored as a hex string, ex 0014A8C0
486 InetAddress gatewayAddr =
487 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
488
489 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
490 routes.add(route);
491 } catch (Exception e) {
492 Log.e(TAG, "Error parsing route " + s + " : " + e);
493 continue;
494 }
495 }
496 }
497 }
498
499 // v6 routes listed as:
500 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
501 for (String s : readRouteList("/proc/net/ipv6_route")) {
502 String[]fields = s.split("\\s+");
503 if (fields.length > 9) {
504 String iface = fields[9].trim();
505 if (interfaceName.equals(iface)) {
506 String dest = fields[0];
507 String prefix = fields[1];
508 String gate = fields[4];
509
510 try {
511 // prefix length stored as a hex string, ex 40
512 int prefixLength = Integer.parseInt(prefix, 16);
513
514 // address stored as a 32 char hex string
515 // ex fe800000000000000000000000000000
516 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
517 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
518
519 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
520
521 RouteInfo route = new RouteInfo(linkAddress, gateAddr);
522 routes.add(route);
523 } catch (Exception e) {
524 Log.e(TAG, "Error parsing route " + s + " : " + e);
525 continue;
526 }
527 }
528 }
529 }
530 return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
531 }
532
San Mehat873f2142010-01-14 10:25:07 -0800533 public void shutdown() {
534 if (mContext.checkCallingOrSelfPermission(
535 android.Manifest.permission.SHUTDOWN)
536 != PackageManager.PERMISSION_GRANTED) {
537 throw new SecurityException("Requires SHUTDOWN permission");
538 }
539
Joe Onorato8a9b2202010-02-26 18:56:32 -0800540 Slog.d(TAG, "Shutting down");
San Mehat873f2142010-01-14 10:25:07 -0800541 }
542
543 public boolean getIpForwardingEnabled() throws IllegalStateException{
544 mContext.enforceCallingOrSelfPermission(
545 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
546
Kenny Roota80ce062010-06-01 13:23:53 -0700547 ArrayList<String> rsp;
548 try {
549 rsp = mConnector.doCommand("ipfwd status");
550 } catch (NativeDaemonConnectorException e) {
551 throw new IllegalStateException(
552 "Unable to communicate with native daemon to ipfwd status");
553 }
San Mehat873f2142010-01-14 10:25:07 -0800554
555 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700556 String[] tok = line.split(" ");
557 if (tok.length < 3) {
558 Slog.e(TAG, "Malformed response from native daemon: " + line);
559 return false;
560 }
561
San Mehat873f2142010-01-14 10:25:07 -0800562 int code = Integer.parseInt(tok[0]);
563 if (code == NetdResponseCode.IpFwdStatusResult) {
564 // 211 Forwarding <enabled/disabled>
Kenny Roota80ce062010-06-01 13:23:53 -0700565 return "enabled".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800566 } else {
567 throw new IllegalStateException(String.format("Unexpected response code %d", code));
568 }
569 }
570 throw new IllegalStateException("Got an empty response");
571 }
572
573 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
574 mContext.enforceCallingOrSelfPermission(
575 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
576 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
577 }
578
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700579 public void startTethering(String[] dhcpRange)
San Mehat873f2142010-01-14 10:25:07 -0800580 throws IllegalStateException {
581 mContext.enforceCallingOrSelfPermission(
582 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700583 // cmd is "tether start first_start first_stop second_start second_stop ..."
584 // an odd number of addrs will fail
585 String cmd = "tether start";
586 for (String d : dhcpRange) {
587 cmd += " " + d;
588 }
Kenny Roota80ce062010-06-01 13:23:53 -0700589
590 try {
591 mConnector.doCommand(cmd);
592 } catch (NativeDaemonConnectorException e) {
593 throw new IllegalStateException("Unable to communicate to native daemon");
594 }
San Mehat873f2142010-01-14 10:25:07 -0800595 }
596
597 public void stopTethering() throws IllegalStateException {
598 mContext.enforceCallingOrSelfPermission(
599 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700600 try {
601 mConnector.doCommand("tether stop");
602 } catch (NativeDaemonConnectorException e) {
603 throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
604 }
San Mehat873f2142010-01-14 10:25:07 -0800605 }
606
607 public boolean isTetheringStarted() throws IllegalStateException {
608 mContext.enforceCallingOrSelfPermission(
609 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
610
Kenny Roota80ce062010-06-01 13:23:53 -0700611 ArrayList<String> rsp;
612 try {
613 rsp = mConnector.doCommand("tether status");
614 } catch (NativeDaemonConnectorException e) {
615 throw new IllegalStateException(
616 "Unable to communicate to native daemon to get tether status");
617 }
San Mehat873f2142010-01-14 10:25:07 -0800618
619 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700620 String[] tok = line.split(" ");
621 if (tok.length < 3) {
622 throw new IllegalStateException("Malformed response for tether status: " + line);
623 }
San Mehat873f2142010-01-14 10:25:07 -0800624 int code = Integer.parseInt(tok[0]);
625 if (code == NetdResponseCode.TetherStatusResult) {
626 // XXX: Tethering services <started/stopped> <TBD>...
Kenny Roota80ce062010-06-01 13:23:53 -0700627 return "started".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800628 } else {
629 throw new IllegalStateException(String.format("Unexpected response code %d", code));
630 }
631 }
632 throw new IllegalStateException("Got an empty response");
633 }
634
635 public void tetherInterface(String iface) throws IllegalStateException {
636 mContext.enforceCallingOrSelfPermission(
637 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700638 try {
639 mConnector.doCommand("tether interface add " + iface);
640 } catch (NativeDaemonConnectorException e) {
641 throw new IllegalStateException(
642 "Unable to communicate to native daemon for adding tether interface");
643 }
San Mehat873f2142010-01-14 10:25:07 -0800644 }
645
646 public void untetherInterface(String iface) {
647 mContext.enforceCallingOrSelfPermission(
648 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700649 try {
650 mConnector.doCommand("tether interface remove " + iface);
651 } catch (NativeDaemonConnectorException e) {
652 throw new IllegalStateException(
653 "Unable to communicate to native daemon for removing tether interface");
654 }
San Mehat873f2142010-01-14 10:25:07 -0800655 }
656
657 public String[] listTetheredInterfaces() throws IllegalStateException {
658 mContext.enforceCallingOrSelfPermission(
659 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700660 try {
661 return mConnector.doListCommand(
662 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
663 } catch (NativeDaemonConnectorException e) {
664 throw new IllegalStateException(
665 "Unable to communicate to native daemon for listing tether interfaces");
666 }
San Mehat873f2142010-01-14 10:25:07 -0800667 }
668
669 public void setDnsForwarders(String[] dns) throws IllegalStateException {
670 mContext.enforceCallingOrSelfPermission(
671 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
672 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800673 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800674 for (String s : dns) {
Robert Greenwalte5903732011-02-22 16:00:42 -0800675 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800676 }
Kenny Roota80ce062010-06-01 13:23:53 -0700677 try {
678 mConnector.doCommand(cmd);
679 } catch (NativeDaemonConnectorException e) {
680 throw new IllegalStateException(
681 "Unable to communicate to native daemon for setting tether dns");
682 }
Robert Greenwalte5903732011-02-22 16:00:42 -0800683 } catch (IllegalArgumentException e) {
San Mehat873f2142010-01-14 10:25:07 -0800684 throw new IllegalStateException("Error resolving dns name", e);
685 }
686 }
687
688 public String[] getDnsForwarders() throws IllegalStateException {
689 mContext.enforceCallingOrSelfPermission(
690 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700691 try {
692 return mConnector.doListCommand(
693 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
694 } catch (NativeDaemonConnectorException e) {
695 throw new IllegalStateException(
696 "Unable to communicate to native daemon for listing tether dns");
697 }
San Mehat873f2142010-01-14 10:25:07 -0800698 }
699
700 public void enableNat(String internalInterface, String externalInterface)
701 throws IllegalStateException {
702 mContext.enforceCallingOrSelfPermission(
703 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700704 try {
705 mConnector.doCommand(
706 String.format("nat enable %s %s", internalInterface, externalInterface));
707 } catch (NativeDaemonConnectorException e) {
708 throw new IllegalStateException(
709 "Unable to communicate to native daemon for enabling NAT interface");
710 }
San Mehat873f2142010-01-14 10:25:07 -0800711 }
712
713 public void disableNat(String internalInterface, String externalInterface)
714 throws IllegalStateException {
715 mContext.enforceCallingOrSelfPermission(
716 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700717 try {
718 mConnector.doCommand(
719 String.format("nat disable %s %s", internalInterface, externalInterface));
720 } catch (NativeDaemonConnectorException e) {
721 throw new IllegalStateException(
722 "Unable to communicate to native daemon for disabling NAT interface");
723 }
San Mehat873f2142010-01-14 10:25:07 -0800724 }
San Mehat72759df2010-01-19 13:50:37 -0800725
726 public String[] listTtys() throws IllegalStateException {
727 mContext.enforceCallingOrSelfPermission(
728 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700729 try {
730 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
731 } catch (NativeDaemonConnectorException e) {
732 throw new IllegalStateException(
733 "Unable to communicate to native daemon for listing TTYs");
734 }
San Mehat72759df2010-01-19 13:50:37 -0800735 }
736
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800737 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
738 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800739 try {
740 mContext.enforceCallingOrSelfPermission(
741 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800742 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
Robert Greenwalte5903732011-02-22 16:00:42 -0800743 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
744 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
745 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
746 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
747 } catch (IllegalArgumentException e) {
San Mehat72759df2010-01-19 13:50:37 -0800748 throw new IllegalStateException("Error resolving addr", e);
Kenny Roota80ce062010-06-01 13:23:53 -0700749 } catch (NativeDaemonConnectorException e) {
750 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
San Mehat72759df2010-01-19 13:50:37 -0800751 }
752 }
753
754 public void detachPppd(String tty) throws IllegalStateException {
755 mContext.enforceCallingOrSelfPermission(
756 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700757 try {
758 mConnector.doCommand(String.format("pppd detach %s", tty));
759 } catch (NativeDaemonConnectorException e) {
760 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
761 }
San Mehat72759df2010-01-19 13:50:37 -0800762 }
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800763
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700764 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800765 throws IllegalStateException {
766 mContext.enforceCallingOrSelfPermission(
767 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
768 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700769 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700770 try {
771 mConnector.doCommand(String.format("softap stop " + wlanIface));
772 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
773 mConnector.doCommand(String.format("softap start " + wlanIface));
774 if (wifiConfig == null) {
775 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
776 } else {
777 /**
778 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
779 * argv1 - wlan interface
780 * argv2 - softap interface
781 * argv3 - SSID
782 * argv4 - Security
783 * argv5 - Key
784 * argv6 - Channel
785 * argv7 - Preamble
786 * argv8 - Max SCB
787 */
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800788 String str = String.format("softap set " + wlanIface + " " + softapIface +
789 " %s %s %s", convertQuotedString(wifiConfig.SSID),
790 getSecurityType(wifiConfig),
791 convertQuotedString(wifiConfig.preSharedKey));
Kenny Roota80ce062010-06-01 13:23:53 -0700792 mConnector.doCommand(str);
793 }
794 mConnector.doCommand(String.format("softap startap"));
795 } catch (NativeDaemonConnectorException e) {
796 throw new IllegalStateException("Error communicating to native daemon to start softap", e);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800797 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800798 }
799
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700800 private String convertQuotedString(String s) {
Irfan Sheriff7baec0f2010-05-26 17:16:47 -0700801 if (s == null) {
802 return s;
803 }
804 /* Replace \ with \\, then " with \" and add quotes at end */
805 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700806 }
807
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800808 private String getSecurityType(WifiConfiguration wifiConfig) {
809 switch (wifiConfig.getAuthType()) {
810 case KeyMgmt.WPA_PSK:
811 return "wpa-psk";
812 case KeyMgmt.WPA2_PSK:
813 return "wpa2-psk";
814 default:
815 return "open";
816 }
817 }
818
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800819 public void stopAccessPoint() throws IllegalStateException {
820 mContext.enforceCallingOrSelfPermission(
821 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
822 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700823 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700824 try {
825 mConnector.doCommand("softap stopap");
826 } catch (NativeDaemonConnectorException e) {
827 throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
828 e);
829 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800830 }
831
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700832 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
833 throws IllegalStateException {
834 mContext.enforceCallingOrSelfPermission(
835 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
836 mContext.enforceCallingOrSelfPermission(
837 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700838 try {
839 if (wifiConfig == null) {
840 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
841 } else {
842 String str = String.format("softap set " + wlanIface + " " + softapIface
843 + " %s %s %s", convertQuotedString(wifiConfig.SSID),
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800844 getSecurityType(wifiConfig),
Kenny Roota80ce062010-06-01 13:23:53 -0700845 convertQuotedString(wifiConfig.preSharedKey));
846 mConnector.doCommand(str);
847 }
848 } catch (NativeDaemonConnectorException e) {
849 throw new IllegalStateException("Error communicating to native daemon to set soft AP",
850 e);
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700851 }
852 }
San Mehat91cac642010-03-31 14:31:36 -0700853
854 private long getInterfaceCounter(String iface, boolean rx) {
855 mContext.enforceCallingOrSelfPermission(
856 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
857 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700858 String rsp;
859 try {
860 rsp = mConnector.doCommand(
861 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
862 } catch (NativeDaemonConnectorException e1) {
863 Slog.e(TAG, "Error communicating with native daemon", e1);
864 return -1;
865 }
866
867 String[] tok = rsp.split(" ");
868 if (tok.length < 2) {
869 Slog.e(TAG, String.format("Malformed response for reading %s interface",
870 (rx ? "rx" : "tx")));
871 return -1;
872 }
873
San Mehat91cac642010-03-31 14:31:36 -0700874 int code;
875 try {
876 code = Integer.parseInt(tok[0]);
877 } catch (NumberFormatException nfe) {
878 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
879 return -1;
880 }
881 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
882 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
883 Slog.e(TAG, String.format("Unexpected response code %d", code));
884 return -1;
885 }
886 return Long.parseLong(tok[1]);
887 } catch (Exception e) {
888 Slog.e(TAG, String.format(
889 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
890 }
891 return -1;
892 }
893
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700894 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700895 public NetworkStats getNetworkStatsSummary() {
896 mContext.enforceCallingOrSelfPermission(
897 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
898
899 final String[] ifaces = listInterfaces();
Jeff Sharkey4a971222011-06-11 22:16:55 -0700900 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700901
902 for (String iface : ifaces) {
903 final long rx = getInterfaceCounter(iface, true);
904 final long tx = getInterfaceCounter(iface, false);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700905 stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700906 }
907
Jeff Sharkey4a971222011-06-11 22:16:55 -0700908 return stats;
San Mehat91cac642010-03-31 14:31:36 -0700909 }
910
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700911 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700912 public NetworkStats getNetworkStatsDetail() {
913 mContext.enforceCallingOrSelfPermission(
914 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
915
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700916 if (mProcStatsNetfilter.exists()) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700917 return getNetworkStatsDetailNetfilter(UID_ALL);
918 } else {
919 return getNetworkStatsDetailUidstat(UID_ALL);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700920 }
San Mehat91cac642010-03-31 14:31:36 -0700921 }
922
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700923 @Override
Ashish Sharma50fd36d2011-06-15 19:34:53 -0700924 public void setInterfaceQuota(String[] iface, long quota)
925 throws IllegalStateException {
926 mContext.enforceCallingOrSelfPermission(
927 android.Manifest.permission.MANAGE_NETWORK_POLICY, "NetworkManagementService");
928 try {
929 // TODO: Add support for clubbing together multiple interfaces under
930 // one quota. Will need support from the kernel and
931 // BandwidthController to do this.
932 mConnector.doCommand(
933 String.format("bandwidth setquota %s %d", iface[0], quota));
934 } catch (NativeDaemonConnectorException e) {
935 throw new IllegalStateException(
936 "Error communicating to native daemon to set Interface quota",
937 e);
938 }
939 }
940
941 @Override
942 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
943 throws IllegalStateException {
944 mContext.enforceCallingOrSelfPermission(
945 android.Manifest.permission.MANAGE_NETWORK_POLICY, "NetworkManagementService");
946 try {
947 // TODO: Connect with BandwidthController
948 // mConnector.doCommand("");
949 } catch (NativeDaemonConnectorException e) {
950 throw new IllegalStateException(
951 "Error communicating to native daemon to set Interface quota",
952 e);
953 }
954 }
955
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700956 public NetworkStats getNetworkStatsUidDetail(int uid) {
957 if (Binder.getCallingUid() != uid) {
958 mContext.enforceCallingOrSelfPermission(
959 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
960 }
961
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700962 if (mProcStatsNetfilter.exists()) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700963 return getNetworkStatsDetailNetfilter(uid);
964 } else {
965 return getNetworkStatsDetailUidstat(uid);
966 }
967 }
968
969 /**
970 * Build {@link NetworkStats} with detailed UID statistics.
971 */
972 private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
973 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700974 final ArrayList<String> keys = Lists.newArrayList();
975 final ArrayList<String> values = Lists.newArrayList();
976 final HashMap<String, String> parsed = Maps.newHashMap();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700977
978 BufferedReader reader = null;
979 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700980 reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700981
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700982 // parse first line as header
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700983 String line = reader.readLine();
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700984 splitLine(line, keys);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700985
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700986 // parse remaining lines
987 while ((line = reader.readLine()) != null) {
988 splitLine(line, values);
989 parseLine(keys, values, parsed);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700990
991 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700992 final String iface = parsed.get(KEY_IFACE);
993 final int tag = BlockGuard.kernelToTag(parsed.get(KEY_TAG_HEX));
994 final int uid = Integer.parseInt(parsed.get(KEY_UID));
995 final long rx = Long.parseLong(parsed.get(KEY_RX));
996 final long tx = Long.parseLong(parsed.get(KEY_TX));
Jeff Sharkeyd03fd3f2011-06-19 20:55:09 -0700997
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700998 if (limitUid == UID_ALL || limitUid == uid) {
999 stats.addEntry(iface, uid, tag, rx, tx);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001000 }
1001 } catch (NumberFormatException e) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001002 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001003 }
1004 }
1005 } catch (IOException e) {
1006 Slog.w(TAG, "problem parsing stats: " + e);
1007 } finally {
1008 IoUtils.closeQuietly(reader);
1009 }
1010
Jeff Sharkey4a971222011-06-11 22:16:55 -07001011 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001012 }
1013
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001014 /**
1015 * Build {@link NetworkStats} with detailed UID statistics.
1016 *
1017 * @deprecated since this uses older "uid_stat" data, and doesn't provide
1018 * tag-level granularity or additional variables.
1019 */
1020 @Deprecated
1021 private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
1022 final String[] knownUids;
1023 if (limitUid == UID_ALL) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001024 knownUids = mProcStatsUidstat.list();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001025 } else {
1026 knownUids = new String[] { String.valueOf(limitUid) };
1027 }
1028
1029 final NetworkStats stats = new NetworkStats(
1030 SystemClock.elapsedRealtime(), knownUids.length);
1031 for (String uid : knownUids) {
1032 final int uidInt = Integer.parseInt(uid);
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001033 final File uidPath = new File(mProcStatsUidstat, uid);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001034 final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
1035 final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
1036 stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
1037 }
1038
1039 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001040 }
1041
San Mehatf0db6e12010-04-07 15:22:10 -07001042 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
San Mehat91cac642010-03-31 14:31:36 -07001043 mContext.enforceCallingOrSelfPermission(
1044 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -07001045 try {
1046 mConnector.doCommand(String.format(
1047 "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
1048 } catch (NativeDaemonConnectorException e) {
1049 Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
1050 }
San Mehat91cac642010-03-31 14:31:36 -07001051 }
1052
1053 private int getInterfaceThrottle(String iface, boolean rx) {
1054 mContext.enforceCallingOrSelfPermission(
1055 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1056 try {
Kenny Roota80ce062010-06-01 13:23:53 -07001057 String rsp;
1058 try {
1059 rsp = mConnector.doCommand(
1060 String.format("interface getthrottle %s %s", iface,
1061 (rx ? "rx" : "tx"))).get(0);
1062 } catch (NativeDaemonConnectorException e) {
1063 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
1064 return -1;
1065 }
1066
1067 String[] tok = rsp.split(" ");
1068 if (tok.length < 2) {
1069 Slog.e(TAG, "Malformed response to getthrottle command");
1070 return -1;
1071 }
1072
San Mehat91cac642010-03-31 14:31:36 -07001073 int code;
1074 try {
1075 code = Integer.parseInt(tok[0]);
1076 } catch (NumberFormatException nfe) {
1077 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
1078 return -1;
1079 }
1080 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
1081 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
1082 Slog.e(TAG, String.format("Unexpected response code %d", code));
1083 return -1;
1084 }
1085 return Integer.parseInt(tok[1]);
1086 } catch (Exception e) {
1087 Slog.e(TAG, String.format(
1088 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
1089 }
1090 return -1;
1091 }
1092
1093 public int getInterfaceRxThrottle(String iface) {
1094 return getInterfaceThrottle(iface, true);
1095 }
1096
1097 public int getInterfaceTxThrottle(String iface) {
1098 return getInterfaceThrottle(iface, false);
1099 }
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001100
Jeff Sharkey0a9ee122011-06-22 16:32:41 -07001101 @Override
1102 public void setBandwidthControlEnabled(boolean enabled) {
1103 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1104 mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
1105 }
1106
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001107 /**
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001108 * Split given line into {@link ArrayList}.
1109 */
1110 private static void splitLine(String line, ArrayList<String> outSplit) {
1111 outSplit.clear();
1112
1113 final StringTokenizer t = new StringTokenizer(line);
1114 while (t.hasMoreTokens()) {
1115 outSplit.add(t.nextToken());
1116 }
1117 }
1118
1119 /**
1120 * Zip the two given {@link ArrayList} as key and value pairs into
1121 * {@link HashMap}.
1122 */
1123 private static void parseLine(
1124 ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
1125 outParsed.clear();
1126
1127 final int size = Math.min(keys.size(), values.size());
1128 for (int i = 0; i < size; i++) {
1129 outParsed.put(keys.get(i), values.get(i));
1130 }
1131 }
1132
1133 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001134 * Utility method to read a single plain-text {@link Long} from the given
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001135 * {@link File}, usually from a {@code /proc/} filesystem.
1136 */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001137 private static long readSingleLongFromFile(File file) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001138 try {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001139 final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
1140 return Long.parseLong(new String(buffer).trim());
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001141 } catch (NumberFormatException e) {
1142 return -1;
1143 } catch (IOException e) {
1144 return -1;
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001145 }
1146 }
San Mehat873f2142010-01-14 10:25:07 -08001147}