blob: adc65708d5ef439b82067fcb0ee7049084cdc366 [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;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070040import android.util.SparseBooleanArray;
San Mehat873f2142010-01-14 10:25:07 -080041
Jeff Sharkey4414cea2011-06-24 17:05:24 -070042import com.google.android.collect.Lists;
43import com.google.android.collect.Maps;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070044import com.google.android.collect.Sets;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070045
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 Sharkeyb3f19ca2011-06-29 23:54:13 -070057import java.util.HashSet;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070058import java.util.NoSuchElementException;
59import java.util.StringTokenizer;
Robert Greenwalte5c3afb2010-09-22 14:32:35 -070060import java.util.concurrent.CountDownLatch;
San Mehat873f2142010-01-14 10:25:07 -080061
Jeff Sharkey9a13f362011-04-26 16:25:36 -070062import libcore.io.IoUtils;
63
San Mehat873f2142010-01-14 10:25:07 -080064/**
65 * @hide
66 */
67class NetworkManagementService extends INetworkManagementService.Stub {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070068 private static final String TAG = "NetworkManagementService";
Dianne Hackborncef65ee2010-09-30 18:27:22 -070069 private static final boolean DBG = false;
Kenny Root305bcbf2010-09-03 07:56:38 -070070 private static final String NETD_TAG = "NetdConnector";
71
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070072 private static final int ADD = 1;
73 private static final int REMOVE = 2;
74
Jeff Sharkey4414cea2011-06-24 17:05:24 -070075 /** Path to {@code /proc/uid_stat}. */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070076 @Deprecated
Jeff Sharkey4414cea2011-06-24 17:05:24 -070077 private final File mProcStatsUidstat;
78 /** Path to {@code /proc/net/xt_qtaguid/stats}. */
79 private final File mProcStatsNetfilter;
80
81 /** {@link #mProcStatsNetfilter} headers. */
82 private static final String KEY_IFACE = "iface";
83 private static final String KEY_TAG_HEX = "acct_tag_hex";
84 private static final String KEY_UID = "uid_tag_int";
85 private static final String KEY_RX = "rx_bytes";
86 private static final String KEY_TX = "tx_bytes";
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070087
San Mehat873f2142010-01-14 10:25:07 -080088 class NetdResponseCode {
89 public static final int InterfaceListResult = 110;
90 public static final int TetherInterfaceListResult = 111;
91 public static final int TetherDnsFwdTgtListResult = 112;
San Mehat72759df2010-01-19 13:50:37 -080092 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080093
94 public static final int TetherStatusResult = 210;
95 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080096 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080097 public static final int SoftapStatusResult = 214;
San Mehat91cac642010-03-31 14:31:36 -070098 public static final int InterfaceRxCounterResult = 216;
99 public static final int InterfaceTxCounterResult = 217;
100 public static final int InterfaceRxThrottleResult = 218;
101 public static final int InterfaceTxThrottleResult = 219;
Robert Greenwalte3253922010-02-18 09:23:25 -0800102
103 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -0800104 }
105
106 /**
107 * Binder context for this service
108 */
109 private Context mContext;
110
111 /**
112 * connector object for communicating with netd
113 */
114 private NativeDaemonConnector mConnector;
115
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700116 private Thread mThread;
117 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
118
San Mehat4d02d002010-01-22 16:07:46 -0800119 private ArrayList<INetworkManagementEventObserver> mObservers;
120
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700121 /** Set of interfaces with active quotas. */
122 private HashSet<String> mInterfaceQuota = Sets.newHashSet();
123 /** Set of UIDs with active reject rules. */
124 private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
125
San Mehat873f2142010-01-14 10:25:07 -0800126 /**
127 * Constructs a new NetworkManagementService instance
128 *
129 * @param context Binder context for this service
130 */
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700131 private NetworkManagementService(Context context, File procRoot) {
San Mehat873f2142010-01-14 10:25:07 -0800132 mContext = context;
San Mehat4d02d002010-01-22 16:07:46 -0800133 mObservers = new ArrayList<INetworkManagementEventObserver>();
134
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700135 mProcStatsUidstat = new File(procRoot, "uid_stat");
136 mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
137
Marco Nelissen62dbb222010-02-18 10:56:30 -0800138 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
139 return;
140 }
141
San Mehat873f2142010-01-14 10:25:07 -0800142 mConnector = new NativeDaemonConnector(
Kenny Root305bcbf2010-09-03 07:56:38 -0700143 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700144 mThread = new Thread(mConnector, NETD_TAG);
145 }
146
147 public static NetworkManagementService create(Context context) throws InterruptedException {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700148 NetworkManagementService service = new NetworkManagementService(
149 context, new File("/proc/"));
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700150 if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
151 service.mThread.start();
152 if (DBG) Slog.d(TAG, "Awaiting socket connection");
153 service.mConnectedSignal.await();
154 if (DBG) Slog.d(TAG, "Connected");
155 return service;
San Mehat873f2142010-01-14 10:25:07 -0800156 }
157
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700158 // @VisibleForTesting
159 public static NetworkManagementService createForTest(Context context, File procRoot) {
160 // TODO: eventually connect with mock netd
161 return new NetworkManagementService(context, procRoot);
162 }
163
San Mehat4d02d002010-01-22 16:07:46 -0800164 public void registerObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800165 Slog.d(TAG, "Registering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800166 mObservers.add(obs);
167 }
168
169 public void unregisterObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800170 Slog.d(TAG, "Unregistering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800171 mObservers.remove(mObservers.indexOf(obs));
172 }
173
174 /**
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700175 * Notify our observers of an interface status change
San Mehat4d02d002010-01-22 16:07:46 -0800176 */
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700177 private void notifyInterfaceStatusChanged(String iface, boolean up) {
San Mehat4d02d002010-01-22 16:07:46 -0800178 for (INetworkManagementEventObserver obs : mObservers) {
179 try {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700180 obs.interfaceStatusChanged(iface, up);
181 } catch (Exception ex) {
182 Slog.w(TAG, "Observer notifier failed", ex);
183 }
184 }
185 }
186
187 /**
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700188 * Notify our observers of an interface link state change
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700189 * (typically, an Ethernet cable has been plugged-in or unplugged).
190 */
191 private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
192 for (INetworkManagementEventObserver obs : mObservers) {
193 try {
194 obs.interfaceLinkStateChanged(iface, up);
San Mehat4d02d002010-01-22 16:07:46 -0800195 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800196 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800197 }
198 }
199 }
200
201 /**
202 * Notify our observers of an interface addition.
203 */
204 private void notifyInterfaceAdded(String iface) {
205 for (INetworkManagementEventObserver obs : mObservers) {
206 try {
207 obs.interfaceAdded(iface);
208 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800209 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800210 }
211 }
212 }
213
214 /**
215 * Notify our observers of an interface removal.
216 */
217 private void notifyInterfaceRemoved(String iface) {
218 for (INetworkManagementEventObserver obs : mObservers) {
219 try {
220 obs.interfaceRemoved(iface);
221 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800222 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800223 }
224 }
225 }
226
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700227 /**
228 * Let us know the daemon is connected
229 */
230 protected void onConnected() {
231 if (DBG) Slog.d(TAG, "onConnected");
232 mConnectedSignal.countDown();
233 }
234
San Mehat4d02d002010-01-22 16:07:46 -0800235
San Mehat873f2142010-01-14 10:25:07 -0800236 //
237 // Netd Callback handling
238 //
239
240 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
241 public void onDaemonConnected() {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700242 NetworkManagementService.this.onConnected();
San Mehat873f2142010-01-14 10:25:07 -0800243 new Thread() {
244 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800245 }
246 }.start();
247 }
248 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800249 if (code == NetdResponseCode.InterfaceChange) {
250 /*
251 * a network interface change occured
252 * Format: "NNN Iface added <name>"
253 * "NNN Iface removed <name>"
254 * "NNN Iface changed <name> <up/down>"
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700255 * "NNN Iface linkstatus <name> <up/down>"
Robert Greenwalte3253922010-02-18 09:23:25 -0800256 */
257 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
258 throw new IllegalStateException(
259 String.format("Invalid event from daemon (%s)", raw));
260 }
261 if (cooked[2].equals("added")) {
262 notifyInterfaceAdded(cooked[3]);
263 return true;
264 } else if (cooked[2].equals("removed")) {
265 notifyInterfaceRemoved(cooked[3]);
266 return true;
267 } else if (cooked[2].equals("changed") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700268 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
269 return true;
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700270 } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700271 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
Robert Greenwalte3253922010-02-18 09:23:25 -0800272 return true;
273 }
274 throw new IllegalStateException(
275 String.format("Invalid event from daemon (%s)", raw));
276 }
277 return false;
San Mehat873f2142010-01-14 10:25:07 -0800278 }
279 }
280
San Mehated4fc8a2010-01-22 12:28:36 -0800281
San Mehat873f2142010-01-14 10:25:07 -0800282 //
283 // INetworkManagementService members
284 //
285
286 public String[] listInterfaces() throws IllegalStateException {
287 mContext.enforceCallingOrSelfPermission(
288 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
289
Kenny Roota80ce062010-06-01 13:23:53 -0700290 try {
291 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
292 } catch (NativeDaemonConnectorException e) {
293 throw new IllegalStateException(
294 "Cannot communicate with native daemon to list interfaces");
295 }
San Mehated4fc8a2010-01-22 12:28:36 -0800296 }
297
298 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
Kenny Roota80ce062010-06-01 13:23:53 -0700299 String rsp;
300 try {
301 rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
302 } catch (NativeDaemonConnectorException e) {
303 throw new IllegalStateException(
304 "Cannot communicate with native daemon to get interface config");
305 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800306 Slog.d(TAG, String.format("rsp <%s>", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800307
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800308 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
San Mehated4fc8a2010-01-22 12:28:36 -0800309 StringTokenizer st = new StringTokenizer(rsp);
310
Kenny Roota80ce062010-06-01 13:23:53 -0700311 InterfaceConfiguration cfg;
San Mehated4fc8a2010-01-22 12:28:36 -0800312 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700313 try {
314 int code = Integer.parseInt(st.nextToken(" "));
315 if (code != NetdResponseCode.InterfaceGetCfgResult) {
316 throw new IllegalStateException(
317 String.format("Expected code %d, but got %d",
318 NetdResponseCode.InterfaceGetCfgResult, code));
319 }
320 } catch (NumberFormatException nfe) {
San Mehated4fc8a2010-01-22 12:28:36 -0800321 throw new IllegalStateException(
Kenny Roota80ce062010-06-01 13:23:53 -0700322 String.format("Invalid response from daemon (%s)", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800323 }
Kenny Roota80ce062010-06-01 13:23:53 -0700324
325 cfg = new InterfaceConfiguration();
326 cfg.hwAddr = st.nextToken(" ");
Robert Greenwalted126402011-01-28 15:34:55 -0800327 InetAddress addr = null;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800328 int prefixLength = 0;
Kenny Roota80ce062010-06-01 13:23:53 -0700329 try {
Robert Greenwalte5903732011-02-22 16:00:42 -0800330 addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
331 } catch (IllegalArgumentException iae) {
332 Slog.e(TAG, "Failed to parse ipaddr", iae);
Kenny Roota80ce062010-06-01 13:23:53 -0700333 }
334
335 try {
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800336 prefixLength = Integer.parseInt(st.nextToken(" "));
337 } catch (NumberFormatException nfe) {
338 Slog.e(TAG, "Failed to parse prefixLength", nfe);
Kenny Roota80ce062010-06-01 13:23:53 -0700339 }
Robert Greenwalt04808c22010-12-13 17:01:41 -0800340
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800341 cfg.addr = new LinkAddress(addr, prefixLength);
Kenny Roota80ce062010-06-01 13:23:53 -0700342 cfg.interfaceFlags = st.nextToken("]").trim() +"]";
343 } catch (NoSuchElementException nsee) {
San Mehated4fc8a2010-01-22 12:28:36 -0800344 throw new IllegalStateException(
345 String.format("Invalid response from daemon (%s)", rsp));
346 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800347 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
San Mehated4fc8a2010-01-22 12:28:36 -0800348 return cfg;
349 }
350
351 public void setInterfaceConfig(
352 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
Robert Greenwalted126402011-01-28 15:34:55 -0800353 LinkAddress linkAddr = cfg.addr;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800354 if (linkAddr == null || linkAddr.getAddress() == null) {
355 throw new IllegalStateException("Null LinkAddress given");
Robert Greenwalted126402011-01-28 15:34:55 -0800356 }
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800357 String cmd = String.format("interface setcfg %s %s %d %s", iface,
358 linkAddr.getAddress().getHostAddress(),
359 linkAddr.getNetworkPrefixLength(),
360 cfg.interfaceFlags);
Kenny Roota80ce062010-06-01 13:23:53 -0700361 try {
362 mConnector.doCommand(cmd);
363 } catch (NativeDaemonConnectorException e) {
364 throw new IllegalStateException(
Robert Greenwalt81d5ad52010-12-16 11:27:38 -0800365 "Unable to communicate with native daemon to interface setcfg - " + e);
Kenny Roota80ce062010-06-01 13:23:53 -0700366 }
San Mehat873f2142010-01-14 10:25:07 -0800367 }
368
Irfan Sherifff5600612011-06-16 10:26:28 -0700369 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
370 IPv6 addresses on interface down, but we need to do full clean up here */
371 public void clearInterfaceAddresses(String iface) throws IllegalStateException {
372 String cmd = String.format("interface clearaddrs %s", iface);
373 try {
374 mConnector.doCommand(cmd);
375 } catch (NativeDaemonConnectorException e) {
376 throw new IllegalStateException(
377 "Unable to communicate with native daemon to interface clearallips - " + e);
378 }
379 }
380
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -0700381 public void addRoute(String interfaceName, RouteInfo route) {
382 modifyRoute(interfaceName, ADD, route);
383 }
384
385 public void removeRoute(String interfaceName, RouteInfo route) {
386 modifyRoute(interfaceName, REMOVE, route);
387 }
388
389 private void modifyRoute(String interfaceName, int action, RouteInfo route) {
390 ArrayList<String> rsp;
391
392 StringBuilder cmd;
393
394 switch (action) {
395 case ADD:
396 {
397 cmd = new StringBuilder("interface route add " + interfaceName);
398 break;
399 }
400 case REMOVE:
401 {
402 cmd = new StringBuilder("interface route remove " + interfaceName);
403 break;
404 }
405 default:
406 throw new IllegalStateException("Unknown action type " + action);
407 }
408
409 // create triplet: dest-ip-addr prefixlength gateway-ip-addr
410 LinkAddress la = route.getDestination();
411 cmd.append(' ');
412 cmd.append(la.getAddress().getHostAddress());
413 cmd.append(' ');
414 cmd.append(la.getNetworkPrefixLength());
415 cmd.append(' ');
416 if (route.getGateway() == null) {
417 if (la.getAddress() instanceof Inet4Address) {
418 cmd.append("0.0.0.0");
419 } else {
420 cmd.append ("::0");
421 }
422 } else {
423 cmd.append(route.getGateway().getHostAddress());
424 }
425 try {
426 rsp = mConnector.doCommand(cmd.toString());
427 } catch (NativeDaemonConnectorException e) {
428 throw new IllegalStateException(
429 "Unable to communicate with native dameon to add routes - "
430 + e);
431 }
432
433 for (String line : rsp) {
434 Log.v(TAG, "add route response is " + line);
435 }
436 }
437
438 private ArrayList<String> readRouteList(String filename) {
439 FileInputStream fstream = null;
440 ArrayList<String> list = new ArrayList<String>();
441
442 try {
443 fstream = new FileInputStream(filename);
444 DataInputStream in = new DataInputStream(fstream);
445 BufferedReader br = new BufferedReader(new InputStreamReader(in));
446 String s;
447
448 // throw away the title line
449
450 while (((s = br.readLine()) != null) && (s.length() != 0)) {
451 list.add(s);
452 }
453 } catch (IOException ex) {
454 // return current list, possibly empty
455 } finally {
456 if (fstream != null) {
457 try {
458 fstream.close();
459 } catch (IOException ex) {}
460 }
461 }
462
463 return list;
464 }
465
466 public RouteInfo[] getRoutes(String interfaceName) {
467 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
468
469 // v4 routes listed as:
470 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
471 for (String s : readRouteList("/proc/net/route")) {
472 String[] fields = s.split("\t");
473
474 if (fields.length > 7) {
475 String iface = fields[0];
476
477 if (interfaceName.equals(iface)) {
478 String dest = fields[1];
479 String gate = fields[2];
480 String flags = fields[3]; // future use?
481 String mask = fields[7];
482 try {
483 // address stored as a hex string, ex: 0014A8C0
484 InetAddress destAddr =
485 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
486 int prefixLength =
487 NetworkUtils.netmaskIntToPrefixLength(
488 (int)Long.parseLong(mask, 16));
489 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
490
491 // address stored as a hex string, ex 0014A8C0
492 InetAddress gatewayAddr =
493 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
494
495 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
496 routes.add(route);
497 } catch (Exception e) {
498 Log.e(TAG, "Error parsing route " + s + " : " + e);
499 continue;
500 }
501 }
502 }
503 }
504
505 // v6 routes listed as:
506 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
507 for (String s : readRouteList("/proc/net/ipv6_route")) {
508 String[]fields = s.split("\\s+");
509 if (fields.length > 9) {
510 String iface = fields[9].trim();
511 if (interfaceName.equals(iface)) {
512 String dest = fields[0];
513 String prefix = fields[1];
514 String gate = fields[4];
515
516 try {
517 // prefix length stored as a hex string, ex 40
518 int prefixLength = Integer.parseInt(prefix, 16);
519
520 // address stored as a 32 char hex string
521 // ex fe800000000000000000000000000000
522 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
523 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
524
525 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
526
527 RouteInfo route = new RouteInfo(linkAddress, gateAddr);
528 routes.add(route);
529 } catch (Exception e) {
530 Log.e(TAG, "Error parsing route " + s + " : " + e);
531 continue;
532 }
533 }
534 }
535 }
536 return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
537 }
538
San Mehat873f2142010-01-14 10:25:07 -0800539 public void shutdown() {
540 if (mContext.checkCallingOrSelfPermission(
541 android.Manifest.permission.SHUTDOWN)
542 != PackageManager.PERMISSION_GRANTED) {
543 throw new SecurityException("Requires SHUTDOWN permission");
544 }
545
Joe Onorato8a9b2202010-02-26 18:56:32 -0800546 Slog.d(TAG, "Shutting down");
San Mehat873f2142010-01-14 10:25:07 -0800547 }
548
549 public boolean getIpForwardingEnabled() throws IllegalStateException{
550 mContext.enforceCallingOrSelfPermission(
551 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
552
Kenny Roota80ce062010-06-01 13:23:53 -0700553 ArrayList<String> rsp;
554 try {
555 rsp = mConnector.doCommand("ipfwd status");
556 } catch (NativeDaemonConnectorException e) {
557 throw new IllegalStateException(
558 "Unable to communicate with native daemon to ipfwd status");
559 }
San Mehat873f2142010-01-14 10:25:07 -0800560
561 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700562 String[] tok = line.split(" ");
563 if (tok.length < 3) {
564 Slog.e(TAG, "Malformed response from native daemon: " + line);
565 return false;
566 }
567
San Mehat873f2142010-01-14 10:25:07 -0800568 int code = Integer.parseInt(tok[0]);
569 if (code == NetdResponseCode.IpFwdStatusResult) {
570 // 211 Forwarding <enabled/disabled>
Kenny Roota80ce062010-06-01 13:23:53 -0700571 return "enabled".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800572 } else {
573 throw new IllegalStateException(String.format("Unexpected response code %d", code));
574 }
575 }
576 throw new IllegalStateException("Got an empty response");
577 }
578
579 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
580 mContext.enforceCallingOrSelfPermission(
581 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
582 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
583 }
584
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700585 public void startTethering(String[] dhcpRange)
San Mehat873f2142010-01-14 10:25:07 -0800586 throws IllegalStateException {
587 mContext.enforceCallingOrSelfPermission(
588 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700589 // cmd is "tether start first_start first_stop second_start second_stop ..."
590 // an odd number of addrs will fail
591 String cmd = "tether start";
592 for (String d : dhcpRange) {
593 cmd += " " + d;
594 }
Kenny Roota80ce062010-06-01 13:23:53 -0700595
596 try {
597 mConnector.doCommand(cmd);
598 } catch (NativeDaemonConnectorException e) {
599 throw new IllegalStateException("Unable to communicate to native daemon");
600 }
San Mehat873f2142010-01-14 10:25:07 -0800601 }
602
603 public void stopTethering() throws IllegalStateException {
604 mContext.enforceCallingOrSelfPermission(
605 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700606 try {
607 mConnector.doCommand("tether stop");
608 } catch (NativeDaemonConnectorException e) {
609 throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
610 }
San Mehat873f2142010-01-14 10:25:07 -0800611 }
612
613 public boolean isTetheringStarted() throws IllegalStateException {
614 mContext.enforceCallingOrSelfPermission(
615 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
616
Kenny Roota80ce062010-06-01 13:23:53 -0700617 ArrayList<String> rsp;
618 try {
619 rsp = mConnector.doCommand("tether status");
620 } catch (NativeDaemonConnectorException e) {
621 throw new IllegalStateException(
622 "Unable to communicate to native daemon to get tether status");
623 }
San Mehat873f2142010-01-14 10:25:07 -0800624
625 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700626 String[] tok = line.split(" ");
627 if (tok.length < 3) {
628 throw new IllegalStateException("Malformed response for tether status: " + line);
629 }
San Mehat873f2142010-01-14 10:25:07 -0800630 int code = Integer.parseInt(tok[0]);
631 if (code == NetdResponseCode.TetherStatusResult) {
632 // XXX: Tethering services <started/stopped> <TBD>...
Kenny Roota80ce062010-06-01 13:23:53 -0700633 return "started".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800634 } else {
635 throw new IllegalStateException(String.format("Unexpected response code %d", code));
636 }
637 }
638 throw new IllegalStateException("Got an empty response");
639 }
640
641 public void tetherInterface(String iface) throws IllegalStateException {
642 mContext.enforceCallingOrSelfPermission(
643 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700644 try {
645 mConnector.doCommand("tether interface add " + iface);
646 } catch (NativeDaemonConnectorException e) {
647 throw new IllegalStateException(
648 "Unable to communicate to native daemon for adding tether interface");
649 }
San Mehat873f2142010-01-14 10:25:07 -0800650 }
651
652 public void untetherInterface(String iface) {
653 mContext.enforceCallingOrSelfPermission(
654 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700655 try {
656 mConnector.doCommand("tether interface remove " + iface);
657 } catch (NativeDaemonConnectorException e) {
658 throw new IllegalStateException(
659 "Unable to communicate to native daemon for removing tether interface");
660 }
San Mehat873f2142010-01-14 10:25:07 -0800661 }
662
663 public String[] listTetheredInterfaces() throws IllegalStateException {
664 mContext.enforceCallingOrSelfPermission(
665 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700666 try {
667 return mConnector.doListCommand(
668 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
669 } catch (NativeDaemonConnectorException e) {
670 throw new IllegalStateException(
671 "Unable to communicate to native daemon for listing tether interfaces");
672 }
San Mehat873f2142010-01-14 10:25:07 -0800673 }
674
675 public void setDnsForwarders(String[] dns) throws IllegalStateException {
676 mContext.enforceCallingOrSelfPermission(
677 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
678 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800679 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800680 for (String s : dns) {
Robert Greenwalte5903732011-02-22 16:00:42 -0800681 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800682 }
Kenny Roota80ce062010-06-01 13:23:53 -0700683 try {
684 mConnector.doCommand(cmd);
685 } catch (NativeDaemonConnectorException e) {
686 throw new IllegalStateException(
687 "Unable to communicate to native daemon for setting tether dns");
688 }
Robert Greenwalte5903732011-02-22 16:00:42 -0800689 } catch (IllegalArgumentException e) {
San Mehat873f2142010-01-14 10:25:07 -0800690 throw new IllegalStateException("Error resolving dns name", e);
691 }
692 }
693
694 public String[] getDnsForwarders() throws IllegalStateException {
695 mContext.enforceCallingOrSelfPermission(
696 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700697 try {
698 return mConnector.doListCommand(
699 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
700 } catch (NativeDaemonConnectorException e) {
701 throw new IllegalStateException(
702 "Unable to communicate to native daemon for listing tether dns");
703 }
San Mehat873f2142010-01-14 10:25:07 -0800704 }
705
706 public void enableNat(String internalInterface, String externalInterface)
707 throws IllegalStateException {
708 mContext.enforceCallingOrSelfPermission(
709 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700710 try {
711 mConnector.doCommand(
712 String.format("nat enable %s %s", internalInterface, externalInterface));
713 } catch (NativeDaemonConnectorException e) {
714 throw new IllegalStateException(
715 "Unable to communicate to native daemon for enabling NAT interface");
716 }
San Mehat873f2142010-01-14 10:25:07 -0800717 }
718
719 public void disableNat(String internalInterface, String externalInterface)
720 throws IllegalStateException {
721 mContext.enforceCallingOrSelfPermission(
722 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700723 try {
724 mConnector.doCommand(
725 String.format("nat disable %s %s", internalInterface, externalInterface));
726 } catch (NativeDaemonConnectorException e) {
727 throw new IllegalStateException(
728 "Unable to communicate to native daemon for disabling NAT interface");
729 }
San Mehat873f2142010-01-14 10:25:07 -0800730 }
San Mehat72759df2010-01-19 13:50:37 -0800731
732 public String[] listTtys() throws IllegalStateException {
733 mContext.enforceCallingOrSelfPermission(
734 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700735 try {
736 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
737 } catch (NativeDaemonConnectorException e) {
738 throw new IllegalStateException(
739 "Unable to communicate to native daemon for listing TTYs");
740 }
San Mehat72759df2010-01-19 13:50:37 -0800741 }
742
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800743 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
744 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800745 try {
746 mContext.enforceCallingOrSelfPermission(
747 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800748 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
Robert Greenwalte5903732011-02-22 16:00:42 -0800749 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
750 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
751 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
752 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
753 } catch (IllegalArgumentException e) {
San Mehat72759df2010-01-19 13:50:37 -0800754 throw new IllegalStateException("Error resolving addr", e);
Kenny Roota80ce062010-06-01 13:23:53 -0700755 } catch (NativeDaemonConnectorException e) {
756 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
San Mehat72759df2010-01-19 13:50:37 -0800757 }
758 }
759
760 public void detachPppd(String tty) throws IllegalStateException {
761 mContext.enforceCallingOrSelfPermission(
762 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700763 try {
764 mConnector.doCommand(String.format("pppd detach %s", tty));
765 } catch (NativeDaemonConnectorException e) {
766 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
767 }
San Mehat72759df2010-01-19 13:50:37 -0800768 }
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800769
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700770 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800771 throws IllegalStateException {
772 mContext.enforceCallingOrSelfPermission(
773 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
774 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700775 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700776 try {
777 mConnector.doCommand(String.format("softap stop " + wlanIface));
778 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
779 mConnector.doCommand(String.format("softap start " + wlanIface));
780 if (wifiConfig == null) {
781 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
782 } else {
783 /**
784 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
785 * argv1 - wlan interface
786 * argv2 - softap interface
787 * argv3 - SSID
788 * argv4 - Security
789 * argv5 - Key
790 * argv6 - Channel
791 * argv7 - Preamble
792 * argv8 - Max SCB
793 */
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800794 String str = String.format("softap set " + wlanIface + " " + softapIface +
795 " %s %s %s", convertQuotedString(wifiConfig.SSID),
796 getSecurityType(wifiConfig),
797 convertQuotedString(wifiConfig.preSharedKey));
Kenny Roota80ce062010-06-01 13:23:53 -0700798 mConnector.doCommand(str);
799 }
800 mConnector.doCommand(String.format("softap startap"));
801 } catch (NativeDaemonConnectorException e) {
802 throw new IllegalStateException("Error communicating to native daemon to start softap", e);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800803 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800804 }
805
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700806 private String convertQuotedString(String s) {
Irfan Sheriff7baec0f2010-05-26 17:16:47 -0700807 if (s == null) {
808 return s;
809 }
810 /* Replace \ with \\, then " with \" and add quotes at end */
811 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700812 }
813
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800814 private String getSecurityType(WifiConfiguration wifiConfig) {
815 switch (wifiConfig.getAuthType()) {
816 case KeyMgmt.WPA_PSK:
817 return "wpa-psk";
818 case KeyMgmt.WPA2_PSK:
819 return "wpa2-psk";
820 default:
821 return "open";
822 }
823 }
824
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800825 public void stopAccessPoint() throws IllegalStateException {
826 mContext.enforceCallingOrSelfPermission(
827 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
828 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700829 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700830 try {
831 mConnector.doCommand("softap stopap");
832 } catch (NativeDaemonConnectorException e) {
833 throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
834 e);
835 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800836 }
837
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700838 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
839 throws IllegalStateException {
840 mContext.enforceCallingOrSelfPermission(
841 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
842 mContext.enforceCallingOrSelfPermission(
843 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700844 try {
845 if (wifiConfig == null) {
846 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
847 } else {
848 String str = String.format("softap set " + wlanIface + " " + softapIface
849 + " %s %s %s", convertQuotedString(wifiConfig.SSID),
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800850 getSecurityType(wifiConfig),
Kenny Roota80ce062010-06-01 13:23:53 -0700851 convertQuotedString(wifiConfig.preSharedKey));
852 mConnector.doCommand(str);
853 }
854 } catch (NativeDaemonConnectorException e) {
855 throw new IllegalStateException("Error communicating to native daemon to set soft AP",
856 e);
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700857 }
858 }
San Mehat91cac642010-03-31 14:31:36 -0700859
860 private long getInterfaceCounter(String iface, boolean rx) {
861 mContext.enforceCallingOrSelfPermission(
862 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
863 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700864 String rsp;
865 try {
866 rsp = mConnector.doCommand(
867 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
868 } catch (NativeDaemonConnectorException e1) {
869 Slog.e(TAG, "Error communicating with native daemon", e1);
870 return -1;
871 }
872
873 String[] tok = rsp.split(" ");
874 if (tok.length < 2) {
875 Slog.e(TAG, String.format("Malformed response for reading %s interface",
876 (rx ? "rx" : "tx")));
877 return -1;
878 }
879
San Mehat91cac642010-03-31 14:31:36 -0700880 int code;
881 try {
882 code = Integer.parseInt(tok[0]);
883 } catch (NumberFormatException nfe) {
884 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
885 return -1;
886 }
887 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
888 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
889 Slog.e(TAG, String.format("Unexpected response code %d", code));
890 return -1;
891 }
892 return Long.parseLong(tok[1]);
893 } catch (Exception e) {
894 Slog.e(TAG, String.format(
895 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
896 }
897 return -1;
898 }
899
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700900 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700901 public NetworkStats getNetworkStatsSummary() {
902 mContext.enforceCallingOrSelfPermission(
903 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
904
905 final String[] ifaces = listInterfaces();
Jeff Sharkey4a971222011-06-11 22:16:55 -0700906 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700907
908 for (String iface : ifaces) {
909 final long rx = getInterfaceCounter(iface, true);
910 final long tx = getInterfaceCounter(iface, false);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700911 stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700912 }
913
Jeff Sharkey4a971222011-06-11 22:16:55 -0700914 return stats;
San Mehat91cac642010-03-31 14:31:36 -0700915 }
916
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700917 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700918 public NetworkStats getNetworkStatsDetail() {
919 mContext.enforceCallingOrSelfPermission(
920 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
921
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700922 if (mProcStatsNetfilter.exists()) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700923 return getNetworkStatsDetailNetfilter(UID_ALL);
924 } else {
925 return getNetworkStatsDetailUidstat(UID_ALL);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700926 }
San Mehat91cac642010-03-31 14:31:36 -0700927 }
928
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700929 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700930 public void setInterfaceQuota(String iface, long quota) {
931 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
932
933 synchronized (mInterfaceQuota) {
934 if (mInterfaceQuota.contains(iface)) {
935 // TODO: eventually consider throwing
936 return;
937 }
938
939 final StringBuilder command = new StringBuilder();
940 command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
941
942 try {
943 // TODO: add support for quota shared across interfaces
944 mConnector.doCommand(command.toString());
945 mInterfaceQuota.add(iface);
946 } catch (NativeDaemonConnectorException e) {
947 throw new IllegalStateException("Error communicating to native daemon", e);
948 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -0700949 }
950 }
951
952 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700953 public void removeInterfaceQuota(String iface) {
954 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
955
956 synchronized (mInterfaceQuota) {
957 if (!mInterfaceQuota.contains(iface)) {
958 // TODO: eventually consider throwing
959 return;
960 }
961
962 final StringBuilder command = new StringBuilder();
963 command.append("bandwidth removeiquota ").append(iface);
964
965 try {
966 // TODO: add support for quota shared across interfaces
967 mConnector.doCommand(command.toString());
968 mInterfaceQuota.remove(iface);
969 } catch (NativeDaemonConnectorException e) {
970 throw new IllegalStateException("Error communicating to native daemon", e);
971 }
972 }
973 }
974
975 @Override
976 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
977 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
978
979 synchronized (mUidRejectOnQuota) {
980 final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
981 if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
982 // TODO: eventually consider throwing
983 return;
984 }
985
986 final StringBuilder command = new StringBuilder();
987 command.append("bandwidth");
988 if (rejectOnQuotaInterfaces) {
989 command.append(" addnaughtyapps");
990 } else {
991 command.append(" removenaughtyapps");
992 }
993 command.append(" ").append(uid);
994
995 try {
996 mConnector.doCommand(command.toString());
997 if (rejectOnQuotaInterfaces) {
998 mUidRejectOnQuota.put(uid, true);
999 } else {
1000 mUidRejectOnQuota.delete(uid);
1001 }
1002 } catch (NativeDaemonConnectorException e) {
1003 throw new IllegalStateException("Error communicating to native daemon", e);
1004 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -07001005 }
1006 }
1007
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001008 public NetworkStats getNetworkStatsUidDetail(int uid) {
1009 if (Binder.getCallingUid() != uid) {
1010 mContext.enforceCallingOrSelfPermission(
1011 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1012 }
1013
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001014 if (mProcStatsNetfilter.exists()) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001015 return getNetworkStatsDetailNetfilter(uid);
1016 } else {
1017 return getNetworkStatsDetailUidstat(uid);
1018 }
1019 }
1020
1021 /**
1022 * Build {@link NetworkStats} with detailed UID statistics.
1023 */
1024 private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
1025 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001026 final ArrayList<String> keys = Lists.newArrayList();
1027 final ArrayList<String> values = Lists.newArrayList();
1028 final HashMap<String, String> parsed = Maps.newHashMap();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001029
1030 BufferedReader reader = null;
1031 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001032 reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001033
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001034 // parse first line as header
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001035 String line = reader.readLine();
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001036 splitLine(line, keys);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001037
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001038 // parse remaining lines
1039 while ((line = reader.readLine()) != null) {
1040 splitLine(line, values);
1041 parseLine(keys, values, parsed);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001042
1043 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001044 final String iface = parsed.get(KEY_IFACE);
Jesse Wilson8568db52011-06-28 19:06:31 -07001045 final int tag = NetworkManagementSocketTagger.kernelToTag(
1046 parsed.get(KEY_TAG_HEX));
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001047 final int uid = Integer.parseInt(parsed.get(KEY_UID));
1048 final long rx = Long.parseLong(parsed.get(KEY_RX));
1049 final long tx = Long.parseLong(parsed.get(KEY_TX));
Jeff Sharkeyd03fd3f2011-06-19 20:55:09 -07001050
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001051 if (limitUid == UID_ALL || limitUid == uid) {
1052 stats.addEntry(iface, uid, tag, rx, tx);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001053 }
1054 } catch (NumberFormatException e) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001055 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001056 }
1057 }
1058 } catch (IOException e) {
1059 Slog.w(TAG, "problem parsing stats: " + e);
1060 } finally {
1061 IoUtils.closeQuietly(reader);
1062 }
1063
Jeff Sharkey4a971222011-06-11 22:16:55 -07001064 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001065 }
1066
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001067 /**
1068 * Build {@link NetworkStats} with detailed UID statistics.
1069 *
1070 * @deprecated since this uses older "uid_stat" data, and doesn't provide
1071 * tag-level granularity or additional variables.
1072 */
1073 @Deprecated
1074 private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
1075 final String[] knownUids;
1076 if (limitUid == UID_ALL) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001077 knownUids = mProcStatsUidstat.list();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001078 } else {
1079 knownUids = new String[] { String.valueOf(limitUid) };
1080 }
1081
1082 final NetworkStats stats = new NetworkStats(
1083 SystemClock.elapsedRealtime(), knownUids.length);
1084 for (String uid : knownUids) {
1085 final int uidInt = Integer.parseInt(uid);
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001086 final File uidPath = new File(mProcStatsUidstat, uid);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001087 final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
1088 final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
1089 stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
1090 }
1091
1092 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001093 }
1094
San Mehatf0db6e12010-04-07 15:22:10 -07001095 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
San Mehat91cac642010-03-31 14:31:36 -07001096 mContext.enforceCallingOrSelfPermission(
1097 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -07001098 try {
1099 mConnector.doCommand(String.format(
1100 "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
1101 } catch (NativeDaemonConnectorException e) {
1102 Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
1103 }
San Mehat91cac642010-03-31 14:31:36 -07001104 }
1105
1106 private int getInterfaceThrottle(String iface, boolean rx) {
1107 mContext.enforceCallingOrSelfPermission(
1108 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1109 try {
Kenny Roota80ce062010-06-01 13:23:53 -07001110 String rsp;
1111 try {
1112 rsp = mConnector.doCommand(
1113 String.format("interface getthrottle %s %s", iface,
1114 (rx ? "rx" : "tx"))).get(0);
1115 } catch (NativeDaemonConnectorException e) {
1116 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
1117 return -1;
1118 }
1119
1120 String[] tok = rsp.split(" ");
1121 if (tok.length < 2) {
1122 Slog.e(TAG, "Malformed response to getthrottle command");
1123 return -1;
1124 }
1125
San Mehat91cac642010-03-31 14:31:36 -07001126 int code;
1127 try {
1128 code = Integer.parseInt(tok[0]);
1129 } catch (NumberFormatException nfe) {
1130 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
1131 return -1;
1132 }
1133 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
1134 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
1135 Slog.e(TAG, String.format("Unexpected response code %d", code));
1136 return -1;
1137 }
1138 return Integer.parseInt(tok[1]);
1139 } catch (Exception e) {
1140 Slog.e(TAG, String.format(
1141 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
1142 }
1143 return -1;
1144 }
1145
1146 public int getInterfaceRxThrottle(String iface) {
1147 return getInterfaceThrottle(iface, true);
1148 }
1149
1150 public int getInterfaceTxThrottle(String iface) {
1151 return getInterfaceThrottle(iface, false);
1152 }
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001153
Jeff Sharkey0a9ee122011-06-22 16:32:41 -07001154 @Override
1155 public void setBandwidthControlEnabled(boolean enabled) {
1156 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1157 mConnector.doCommand(String.format("bandwidth %s", (enabled ? "enable" : "disable")));
1158 }
1159
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001160 /**
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001161 * Split given line into {@link ArrayList}.
1162 */
1163 private static void splitLine(String line, ArrayList<String> outSplit) {
1164 outSplit.clear();
1165
1166 final StringTokenizer t = new StringTokenizer(line);
1167 while (t.hasMoreTokens()) {
1168 outSplit.add(t.nextToken());
1169 }
1170 }
1171
1172 /**
1173 * Zip the two given {@link ArrayList} as key and value pairs into
1174 * {@link HashMap}.
1175 */
1176 private static void parseLine(
1177 ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
1178 outParsed.clear();
1179
1180 final int size = Math.min(keys.size(), values.size());
1181 for (int i = 0; i < size; i++) {
1182 outParsed.put(keys.get(i), values.get(i));
1183 }
1184 }
1185
1186 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001187 * Utility method to read a single plain-text {@link Long} from the given
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001188 * {@link File}, usually from a {@code /proc/} filesystem.
1189 */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001190 private static long readSingleLongFromFile(File file) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001191 try {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001192 final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
1193 return Long.parseLong(new String(buffer).trim());
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001194 } catch (NumberFormatException e) {
1195 return -1;
1196 } catch (IOException e) {
1197 return -1;
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001198 }
1199 }
San Mehat873f2142010-01-14 10:25:07 -08001200}