blob: 1c150f8a65eacf39159e08a1d6dceca121c88693 [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 Sharkey350083e2011-06-29 10:45:16 -070019import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070020import static android.net.NetworkStats.IFACE_ALL;
21import static android.net.NetworkStats.TAG_NONE;
22import static android.net.NetworkStats.UID_ALL;
Jeff Sharkey350083e2011-06-29 10:45:16 -070023import static android.provider.Settings.Secure.NETSTATS_ENABLED;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070024
San Mehat873f2142010-01-14 10:25:07 -080025import android.content.Context;
San Mehat873f2142010-01-14 10:25:07 -080026import android.content.pm.PackageManager;
San Mehat4d02d002010-01-22 16:07:46 -080027import android.net.INetworkManagementEventObserver;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070028import android.net.InterfaceConfiguration;
Robert Greenwalted126402011-01-28 15:34:55 -080029import android.net.LinkAddress;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070030import android.net.NetworkStats;
Robert Greenwalted126402011-01-28 15:34:55 -080031import android.net.NetworkUtils;
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070032import android.net.RouteInfo;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080033import android.net.wifi.WifiConfiguration;
34import android.net.wifi.WifiConfiguration.KeyMgmt;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070035import android.os.Binder;
San Mehat873f2142010-01-14 10:25:07 -080036import android.os.INetworkManagementService;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070037import android.os.SystemClock;
Marco Nelissen62dbb222010-02-18 10:56:30 -080038import android.os.SystemProperties;
Jeff Sharkey350083e2011-06-29 10:45:16 -070039import android.provider.Settings;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080040import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080041import android.util.Slog;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070042import android.util.SparseBooleanArray;
San Mehat873f2142010-01-14 10:25:07 -080043
Jeff Sharkey4414cea2011-06-24 17:05:24 -070044import com.google.android.collect.Lists;
45import com.google.android.collect.Maps;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070046import com.google.android.collect.Sets;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070047
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070048import java.io.BufferedReader;
49import java.io.DataInputStream;
San Mehat873f2142010-01-14 10:25:07 -080050import java.io.File;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070051import java.io.FileInputStream;
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070052import java.io.FileReader;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070053import java.io.IOException;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070054import java.io.InputStreamReader;
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070055import java.net.Inet4Address;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070056import java.net.InetAddress;
57import java.util.ArrayList;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070058import java.util.HashMap;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070059import java.util.HashSet;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070060import java.util.NoSuchElementException;
61import java.util.StringTokenizer;
Robert Greenwalte5c3afb2010-09-22 14:32:35 -070062import java.util.concurrent.CountDownLatch;
San Mehat873f2142010-01-14 10:25:07 -080063
Jeff Sharkey9a13f362011-04-26 16:25:36 -070064import libcore.io.IoUtils;
65
San Mehat873f2142010-01-14 10:25:07 -080066/**
67 * @hide
68 */
69class NetworkManagementService extends INetworkManagementService.Stub {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070070 private static final String TAG = "NetworkManagementService";
Dianne Hackborncef65ee2010-09-30 18:27:22 -070071 private static final boolean DBG = false;
Kenny Root305bcbf2010-09-03 07:56:38 -070072 private static final String NETD_TAG = "NetdConnector";
73
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070074 private static final int ADD = 1;
75 private static final int REMOVE = 2;
76
Jeff Sharkey4414cea2011-06-24 17:05:24 -070077 /** Path to {@code /proc/uid_stat}. */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070078 @Deprecated
Jeff Sharkey4414cea2011-06-24 17:05:24 -070079 private final File mProcStatsUidstat;
80 /** Path to {@code /proc/net/xt_qtaguid/stats}. */
81 private final File mProcStatsNetfilter;
82
83 /** {@link #mProcStatsNetfilter} headers. */
84 private static final String KEY_IFACE = "iface";
85 private static final String KEY_TAG_HEX = "acct_tag_hex";
86 private static final String KEY_UID = "uid_tag_int";
87 private static final String KEY_RX = "rx_bytes";
88 private static final String KEY_TX = "tx_bytes";
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070089
San Mehat873f2142010-01-14 10:25:07 -080090 class NetdResponseCode {
91 public static final int InterfaceListResult = 110;
92 public static final int TetherInterfaceListResult = 111;
93 public static final int TetherDnsFwdTgtListResult = 112;
San Mehat72759df2010-01-19 13:50:37 -080094 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080095
96 public static final int TetherStatusResult = 210;
97 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080098 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080099 public static final int SoftapStatusResult = 214;
San Mehat91cac642010-03-31 14:31:36 -0700100 public static final int InterfaceRxCounterResult = 216;
101 public static final int InterfaceTxCounterResult = 217;
102 public static final int InterfaceRxThrottleResult = 218;
103 public static final int InterfaceTxThrottleResult = 219;
Robert Greenwalte3253922010-02-18 09:23:25 -0800104
105 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -0800106 }
107
108 /**
109 * Binder context for this service
110 */
111 private Context mContext;
112
113 /**
114 * connector object for communicating with netd
115 */
116 private NativeDaemonConnector mConnector;
117
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700118 private Thread mThread;
119 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
120
San Mehat4d02d002010-01-22 16:07:46 -0800121 private ArrayList<INetworkManagementEventObserver> mObservers;
122
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700123 /** Set of interfaces with active quotas. */
124 private HashSet<String> mInterfaceQuota = Sets.newHashSet();
125 /** Set of UIDs with active reject rules. */
126 private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
127
Jeff Sharkey350083e2011-06-29 10:45:16 -0700128 private boolean mBandwidthControlEnabled;
129
San Mehat873f2142010-01-14 10:25:07 -0800130 /**
131 * Constructs a new NetworkManagementService instance
132 *
133 * @param context Binder context for this service
134 */
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700135 private NetworkManagementService(Context context, File procRoot) {
San Mehat873f2142010-01-14 10:25:07 -0800136 mContext = context;
San Mehat4d02d002010-01-22 16:07:46 -0800137 mObservers = new ArrayList<INetworkManagementEventObserver>();
138
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700139 mProcStatsUidstat = new File(procRoot, "uid_stat");
140 mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
141
Marco Nelissen62dbb222010-02-18 10:56:30 -0800142 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
143 return;
144 }
145
San Mehat873f2142010-01-14 10:25:07 -0800146 mConnector = new NativeDaemonConnector(
Kenny Root305bcbf2010-09-03 07:56:38 -0700147 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700148 mThread = new Thread(mConnector, NETD_TAG);
149 }
150
151 public static NetworkManagementService create(Context context) throws InterruptedException {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700152 NetworkManagementService service = new NetworkManagementService(
153 context, new File("/proc/"));
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700154 if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
155 service.mThread.start();
156 if (DBG) Slog.d(TAG, "Awaiting socket connection");
157 service.mConnectedSignal.await();
158 if (DBG) Slog.d(TAG, "Connected");
159 return service;
San Mehat873f2142010-01-14 10:25:07 -0800160 }
161
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700162 // @VisibleForTesting
163 public static NetworkManagementService createForTest(Context context, File procRoot) {
164 // TODO: eventually connect with mock netd
165 return new NetworkManagementService(context, procRoot);
166 }
167
Jeff Sharkey350083e2011-06-29 10:45:16 -0700168 public void systemReady() {
169
170 // only enable bandwidth control when support exists, and requested by
171 // system setting.
172 // TODO: eventually migrate to be always enabled
173 final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
174 final boolean shouldEnable =
175 Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0;
176
177 mBandwidthControlEnabled = false;
178 if (hasKernelSupport && shouldEnable) {
179 Slog.d(TAG, "enabling bandwidth control");
180 try {
181 mConnector.doCommand("bandwidth enable");
182 mBandwidthControlEnabled = true;
183 } catch (NativeDaemonConnectorException e) {
184 Slog.e(TAG, "problem enabling bandwidth controls", e);
185 }
186 } else {
187 Slog.d(TAG, "not enabling bandwidth control");
188 }
189 }
190
San Mehat4d02d002010-01-22 16:07:46 -0800191 public void registerObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800192 Slog.d(TAG, "Registering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800193 mObservers.add(obs);
194 }
195
196 public void unregisterObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800197 Slog.d(TAG, "Unregistering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800198 mObservers.remove(mObservers.indexOf(obs));
199 }
200
201 /**
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700202 * Notify our observers of an interface status change
San Mehat4d02d002010-01-22 16:07:46 -0800203 */
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700204 private void notifyInterfaceStatusChanged(String iface, boolean up) {
San Mehat4d02d002010-01-22 16:07:46 -0800205 for (INetworkManagementEventObserver obs : mObservers) {
206 try {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700207 obs.interfaceStatusChanged(iface, up);
208 } catch (Exception ex) {
209 Slog.w(TAG, "Observer notifier failed", ex);
210 }
211 }
212 }
213
214 /**
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700215 * Notify our observers of an interface link state change
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700216 * (typically, an Ethernet cable has been plugged-in or unplugged).
217 */
218 private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
219 for (INetworkManagementEventObserver obs : mObservers) {
220 try {
221 obs.interfaceLinkStateChanged(iface, up);
San Mehat4d02d002010-01-22 16:07:46 -0800222 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800223 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800224 }
225 }
226 }
227
228 /**
229 * Notify our observers of an interface addition.
230 */
231 private void notifyInterfaceAdded(String iface) {
232 for (INetworkManagementEventObserver obs : mObservers) {
233 try {
234 obs.interfaceAdded(iface);
235 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800236 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800237 }
238 }
239 }
240
241 /**
242 * Notify our observers of an interface removal.
243 */
244 private void notifyInterfaceRemoved(String iface) {
245 for (INetworkManagementEventObserver obs : mObservers) {
246 try {
247 obs.interfaceRemoved(iface);
248 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800249 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800250 }
251 }
252 }
253
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700254 /**
255 * Let us know the daemon is connected
256 */
257 protected void onConnected() {
258 if (DBG) Slog.d(TAG, "onConnected");
259 mConnectedSignal.countDown();
260 }
261
San Mehat4d02d002010-01-22 16:07:46 -0800262
San Mehat873f2142010-01-14 10:25:07 -0800263 //
264 // Netd Callback handling
265 //
266
267 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
268 public void onDaemonConnected() {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700269 NetworkManagementService.this.onConnected();
San Mehat873f2142010-01-14 10:25:07 -0800270 new Thread() {
271 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800272 }
273 }.start();
274 }
275 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800276 if (code == NetdResponseCode.InterfaceChange) {
277 /*
278 * a network interface change occured
279 * Format: "NNN Iface added <name>"
280 * "NNN Iface removed <name>"
281 * "NNN Iface changed <name> <up/down>"
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700282 * "NNN Iface linkstatus <name> <up/down>"
Robert Greenwalte3253922010-02-18 09:23:25 -0800283 */
284 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
285 throw new IllegalStateException(
286 String.format("Invalid event from daemon (%s)", raw));
287 }
288 if (cooked[2].equals("added")) {
289 notifyInterfaceAdded(cooked[3]);
290 return true;
291 } else if (cooked[2].equals("removed")) {
292 notifyInterfaceRemoved(cooked[3]);
293 return true;
294 } else if (cooked[2].equals("changed") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700295 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
296 return true;
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700297 } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700298 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
Robert Greenwalte3253922010-02-18 09:23:25 -0800299 return true;
300 }
301 throw new IllegalStateException(
302 String.format("Invalid event from daemon (%s)", raw));
303 }
304 return false;
San Mehat873f2142010-01-14 10:25:07 -0800305 }
306 }
307
San Mehated4fc8a2010-01-22 12:28:36 -0800308
San Mehat873f2142010-01-14 10:25:07 -0800309 //
310 // INetworkManagementService members
311 //
312
313 public String[] listInterfaces() throws IllegalStateException {
314 mContext.enforceCallingOrSelfPermission(
315 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
316
Kenny Roota80ce062010-06-01 13:23:53 -0700317 try {
318 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
319 } catch (NativeDaemonConnectorException e) {
320 throw new IllegalStateException(
321 "Cannot communicate with native daemon to list interfaces");
322 }
San Mehated4fc8a2010-01-22 12:28:36 -0800323 }
324
325 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
Kenny Roota80ce062010-06-01 13:23:53 -0700326 String rsp;
327 try {
328 rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
329 } catch (NativeDaemonConnectorException e) {
330 throw new IllegalStateException(
331 "Cannot communicate with native daemon to get interface config");
332 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800333 Slog.d(TAG, String.format("rsp <%s>", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800334
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800335 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
San Mehated4fc8a2010-01-22 12:28:36 -0800336 StringTokenizer st = new StringTokenizer(rsp);
337
Kenny Roota80ce062010-06-01 13:23:53 -0700338 InterfaceConfiguration cfg;
San Mehated4fc8a2010-01-22 12:28:36 -0800339 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700340 try {
341 int code = Integer.parseInt(st.nextToken(" "));
342 if (code != NetdResponseCode.InterfaceGetCfgResult) {
343 throw new IllegalStateException(
344 String.format("Expected code %d, but got %d",
345 NetdResponseCode.InterfaceGetCfgResult, code));
346 }
347 } catch (NumberFormatException nfe) {
San Mehated4fc8a2010-01-22 12:28:36 -0800348 throw new IllegalStateException(
Kenny Roota80ce062010-06-01 13:23:53 -0700349 String.format("Invalid response from daemon (%s)", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800350 }
Kenny Roota80ce062010-06-01 13:23:53 -0700351
352 cfg = new InterfaceConfiguration();
353 cfg.hwAddr = st.nextToken(" ");
Robert Greenwalted126402011-01-28 15:34:55 -0800354 InetAddress addr = null;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800355 int prefixLength = 0;
Kenny Roota80ce062010-06-01 13:23:53 -0700356 try {
Robert Greenwalte5903732011-02-22 16:00:42 -0800357 addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
358 } catch (IllegalArgumentException iae) {
359 Slog.e(TAG, "Failed to parse ipaddr", iae);
Kenny Roota80ce062010-06-01 13:23:53 -0700360 }
361
362 try {
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800363 prefixLength = Integer.parseInt(st.nextToken(" "));
364 } catch (NumberFormatException nfe) {
365 Slog.e(TAG, "Failed to parse prefixLength", nfe);
Kenny Roota80ce062010-06-01 13:23:53 -0700366 }
Robert Greenwalt04808c22010-12-13 17:01:41 -0800367
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800368 cfg.addr = new LinkAddress(addr, prefixLength);
Kenny Roota80ce062010-06-01 13:23:53 -0700369 cfg.interfaceFlags = st.nextToken("]").trim() +"]";
370 } catch (NoSuchElementException nsee) {
San Mehated4fc8a2010-01-22 12:28:36 -0800371 throw new IllegalStateException(
372 String.format("Invalid response from daemon (%s)", rsp));
373 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800374 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
San Mehated4fc8a2010-01-22 12:28:36 -0800375 return cfg;
376 }
377
378 public void setInterfaceConfig(
379 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
Robert Greenwalted126402011-01-28 15:34:55 -0800380 LinkAddress linkAddr = cfg.addr;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800381 if (linkAddr == null || linkAddr.getAddress() == null) {
382 throw new IllegalStateException("Null LinkAddress given");
Robert Greenwalted126402011-01-28 15:34:55 -0800383 }
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800384 String cmd = String.format("interface setcfg %s %s %d %s", iface,
385 linkAddr.getAddress().getHostAddress(),
386 linkAddr.getNetworkPrefixLength(),
387 cfg.interfaceFlags);
Kenny Roota80ce062010-06-01 13:23:53 -0700388 try {
389 mConnector.doCommand(cmd);
390 } catch (NativeDaemonConnectorException e) {
391 throw new IllegalStateException(
Robert Greenwalt81d5ad52010-12-16 11:27:38 -0800392 "Unable to communicate with native daemon to interface setcfg - " + e);
Kenny Roota80ce062010-06-01 13:23:53 -0700393 }
San Mehat873f2142010-01-14 10:25:07 -0800394 }
395
Irfan Sherifff5600612011-06-16 10:26:28 -0700396 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
397 IPv6 addresses on interface down, but we need to do full clean up here */
398 public void clearInterfaceAddresses(String iface) throws IllegalStateException {
399 String cmd = String.format("interface clearaddrs %s", iface);
400 try {
401 mConnector.doCommand(cmd);
402 } catch (NativeDaemonConnectorException e) {
403 throw new IllegalStateException(
404 "Unable to communicate with native daemon to interface clearallips - " + e);
405 }
406 }
407
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -0700408 public void addRoute(String interfaceName, RouteInfo route) {
409 modifyRoute(interfaceName, ADD, route);
410 }
411
412 public void removeRoute(String interfaceName, RouteInfo route) {
413 modifyRoute(interfaceName, REMOVE, route);
414 }
415
416 private void modifyRoute(String interfaceName, int action, RouteInfo route) {
417 ArrayList<String> rsp;
418
419 StringBuilder cmd;
420
421 switch (action) {
422 case ADD:
423 {
424 cmd = new StringBuilder("interface route add " + interfaceName);
425 break;
426 }
427 case REMOVE:
428 {
429 cmd = new StringBuilder("interface route remove " + interfaceName);
430 break;
431 }
432 default:
433 throw new IllegalStateException("Unknown action type " + action);
434 }
435
436 // create triplet: dest-ip-addr prefixlength gateway-ip-addr
437 LinkAddress la = route.getDestination();
438 cmd.append(' ');
439 cmd.append(la.getAddress().getHostAddress());
440 cmd.append(' ');
441 cmd.append(la.getNetworkPrefixLength());
442 cmd.append(' ');
443 if (route.getGateway() == null) {
444 if (la.getAddress() instanceof Inet4Address) {
445 cmd.append("0.0.0.0");
446 } else {
447 cmd.append ("::0");
448 }
449 } else {
450 cmd.append(route.getGateway().getHostAddress());
451 }
452 try {
453 rsp = mConnector.doCommand(cmd.toString());
454 } catch (NativeDaemonConnectorException e) {
455 throw new IllegalStateException(
456 "Unable to communicate with native dameon to add routes - "
457 + e);
458 }
459
460 for (String line : rsp) {
461 Log.v(TAG, "add route response is " + line);
462 }
463 }
464
465 private ArrayList<String> readRouteList(String filename) {
466 FileInputStream fstream = null;
467 ArrayList<String> list = new ArrayList<String>();
468
469 try {
470 fstream = new FileInputStream(filename);
471 DataInputStream in = new DataInputStream(fstream);
472 BufferedReader br = new BufferedReader(new InputStreamReader(in));
473 String s;
474
475 // throw away the title line
476
477 while (((s = br.readLine()) != null) && (s.length() != 0)) {
478 list.add(s);
479 }
480 } catch (IOException ex) {
481 // return current list, possibly empty
482 } finally {
483 if (fstream != null) {
484 try {
485 fstream.close();
486 } catch (IOException ex) {}
487 }
488 }
489
490 return list;
491 }
492
493 public RouteInfo[] getRoutes(String interfaceName) {
494 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
495
496 // v4 routes listed as:
497 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
498 for (String s : readRouteList("/proc/net/route")) {
499 String[] fields = s.split("\t");
500
501 if (fields.length > 7) {
502 String iface = fields[0];
503
504 if (interfaceName.equals(iface)) {
505 String dest = fields[1];
506 String gate = fields[2];
507 String flags = fields[3]; // future use?
508 String mask = fields[7];
509 try {
510 // address stored as a hex string, ex: 0014A8C0
511 InetAddress destAddr =
512 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
513 int prefixLength =
514 NetworkUtils.netmaskIntToPrefixLength(
515 (int)Long.parseLong(mask, 16));
516 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
517
518 // address stored as a hex string, ex 0014A8C0
519 InetAddress gatewayAddr =
520 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
521
522 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
523 routes.add(route);
524 } catch (Exception e) {
525 Log.e(TAG, "Error parsing route " + s + " : " + e);
526 continue;
527 }
528 }
529 }
530 }
531
532 // v6 routes listed as:
533 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
534 for (String s : readRouteList("/proc/net/ipv6_route")) {
535 String[]fields = s.split("\\s+");
536 if (fields.length > 9) {
537 String iface = fields[9].trim();
538 if (interfaceName.equals(iface)) {
539 String dest = fields[0];
540 String prefix = fields[1];
541 String gate = fields[4];
542
543 try {
544 // prefix length stored as a hex string, ex 40
545 int prefixLength = Integer.parseInt(prefix, 16);
546
547 // address stored as a 32 char hex string
548 // ex fe800000000000000000000000000000
549 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
550 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
551
552 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
553
554 RouteInfo route = new RouteInfo(linkAddress, gateAddr);
555 routes.add(route);
556 } catch (Exception e) {
557 Log.e(TAG, "Error parsing route " + s + " : " + e);
558 continue;
559 }
560 }
561 }
562 }
563 return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
564 }
565
San Mehat873f2142010-01-14 10:25:07 -0800566 public void shutdown() {
567 if (mContext.checkCallingOrSelfPermission(
568 android.Manifest.permission.SHUTDOWN)
569 != PackageManager.PERMISSION_GRANTED) {
570 throw new SecurityException("Requires SHUTDOWN permission");
571 }
572
Joe Onorato8a9b2202010-02-26 18:56:32 -0800573 Slog.d(TAG, "Shutting down");
San Mehat873f2142010-01-14 10:25:07 -0800574 }
575
576 public boolean getIpForwardingEnabled() throws IllegalStateException{
577 mContext.enforceCallingOrSelfPermission(
578 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
579
Kenny Roota80ce062010-06-01 13:23:53 -0700580 ArrayList<String> rsp;
581 try {
582 rsp = mConnector.doCommand("ipfwd status");
583 } catch (NativeDaemonConnectorException e) {
584 throw new IllegalStateException(
585 "Unable to communicate with native daemon to ipfwd status");
586 }
San Mehat873f2142010-01-14 10:25:07 -0800587
588 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700589 String[] tok = line.split(" ");
590 if (tok.length < 3) {
591 Slog.e(TAG, "Malformed response from native daemon: " + line);
592 return false;
593 }
594
San Mehat873f2142010-01-14 10:25:07 -0800595 int code = Integer.parseInt(tok[0]);
596 if (code == NetdResponseCode.IpFwdStatusResult) {
597 // 211 Forwarding <enabled/disabled>
Kenny Roota80ce062010-06-01 13:23:53 -0700598 return "enabled".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800599 } else {
600 throw new IllegalStateException(String.format("Unexpected response code %d", code));
601 }
602 }
603 throw new IllegalStateException("Got an empty response");
604 }
605
606 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
607 mContext.enforceCallingOrSelfPermission(
608 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
609 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
610 }
611
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700612 public void startTethering(String[] dhcpRange)
San Mehat873f2142010-01-14 10:25:07 -0800613 throws IllegalStateException {
614 mContext.enforceCallingOrSelfPermission(
615 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700616 // cmd is "tether start first_start first_stop second_start second_stop ..."
617 // an odd number of addrs will fail
618 String cmd = "tether start";
619 for (String d : dhcpRange) {
620 cmd += " " + d;
621 }
Kenny Roota80ce062010-06-01 13:23:53 -0700622
623 try {
624 mConnector.doCommand(cmd);
625 } catch (NativeDaemonConnectorException e) {
626 throw new IllegalStateException("Unable to communicate to native daemon");
627 }
San Mehat873f2142010-01-14 10:25:07 -0800628 }
629
630 public void stopTethering() throws IllegalStateException {
631 mContext.enforceCallingOrSelfPermission(
632 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700633 try {
634 mConnector.doCommand("tether stop");
635 } catch (NativeDaemonConnectorException e) {
636 throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
637 }
San Mehat873f2142010-01-14 10:25:07 -0800638 }
639
640 public boolean isTetheringStarted() throws IllegalStateException {
641 mContext.enforceCallingOrSelfPermission(
642 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
643
Kenny Roota80ce062010-06-01 13:23:53 -0700644 ArrayList<String> rsp;
645 try {
646 rsp = mConnector.doCommand("tether status");
647 } catch (NativeDaemonConnectorException e) {
648 throw new IllegalStateException(
649 "Unable to communicate to native daemon to get tether status");
650 }
San Mehat873f2142010-01-14 10:25:07 -0800651
652 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700653 String[] tok = line.split(" ");
654 if (tok.length < 3) {
655 throw new IllegalStateException("Malformed response for tether status: " + line);
656 }
San Mehat873f2142010-01-14 10:25:07 -0800657 int code = Integer.parseInt(tok[0]);
658 if (code == NetdResponseCode.TetherStatusResult) {
659 // XXX: Tethering services <started/stopped> <TBD>...
Kenny Roota80ce062010-06-01 13:23:53 -0700660 return "started".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800661 } else {
662 throw new IllegalStateException(String.format("Unexpected response code %d", code));
663 }
664 }
665 throw new IllegalStateException("Got an empty response");
666 }
667
668 public void tetherInterface(String iface) throws IllegalStateException {
669 mContext.enforceCallingOrSelfPermission(
670 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700671 try {
672 mConnector.doCommand("tether interface add " + iface);
673 } catch (NativeDaemonConnectorException e) {
674 throw new IllegalStateException(
675 "Unable to communicate to native daemon for adding tether interface");
676 }
San Mehat873f2142010-01-14 10:25:07 -0800677 }
678
679 public void untetherInterface(String iface) {
680 mContext.enforceCallingOrSelfPermission(
681 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700682 try {
683 mConnector.doCommand("tether interface remove " + iface);
684 } catch (NativeDaemonConnectorException e) {
685 throw new IllegalStateException(
686 "Unable to communicate to native daemon for removing tether interface");
687 }
San Mehat873f2142010-01-14 10:25:07 -0800688 }
689
690 public String[] listTetheredInterfaces() throws IllegalStateException {
691 mContext.enforceCallingOrSelfPermission(
692 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700693 try {
694 return mConnector.doListCommand(
695 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
696 } catch (NativeDaemonConnectorException e) {
697 throw new IllegalStateException(
698 "Unable to communicate to native daemon for listing tether interfaces");
699 }
San Mehat873f2142010-01-14 10:25:07 -0800700 }
701
702 public void setDnsForwarders(String[] dns) throws IllegalStateException {
703 mContext.enforceCallingOrSelfPermission(
704 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
705 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800706 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800707 for (String s : dns) {
Robert Greenwalte5903732011-02-22 16:00:42 -0800708 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800709 }
Kenny Roota80ce062010-06-01 13:23:53 -0700710 try {
711 mConnector.doCommand(cmd);
712 } catch (NativeDaemonConnectorException e) {
713 throw new IllegalStateException(
714 "Unable to communicate to native daemon for setting tether dns");
715 }
Robert Greenwalte5903732011-02-22 16:00:42 -0800716 } catch (IllegalArgumentException e) {
San Mehat873f2142010-01-14 10:25:07 -0800717 throw new IllegalStateException("Error resolving dns name", e);
718 }
719 }
720
721 public String[] getDnsForwarders() throws IllegalStateException {
722 mContext.enforceCallingOrSelfPermission(
723 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700724 try {
725 return mConnector.doListCommand(
726 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
727 } catch (NativeDaemonConnectorException e) {
728 throw new IllegalStateException(
729 "Unable to communicate to native daemon for listing tether dns");
730 }
San Mehat873f2142010-01-14 10:25:07 -0800731 }
732
733 public void enableNat(String internalInterface, String externalInterface)
734 throws IllegalStateException {
735 mContext.enforceCallingOrSelfPermission(
736 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700737 try {
738 mConnector.doCommand(
739 String.format("nat enable %s %s", internalInterface, externalInterface));
740 } catch (NativeDaemonConnectorException e) {
741 throw new IllegalStateException(
742 "Unable to communicate to native daemon for enabling NAT interface");
743 }
San Mehat873f2142010-01-14 10:25:07 -0800744 }
745
746 public void disableNat(String internalInterface, String externalInterface)
747 throws IllegalStateException {
748 mContext.enforceCallingOrSelfPermission(
749 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700750 try {
751 mConnector.doCommand(
752 String.format("nat disable %s %s", internalInterface, externalInterface));
753 } catch (NativeDaemonConnectorException e) {
754 throw new IllegalStateException(
755 "Unable to communicate to native daemon for disabling NAT interface");
756 }
San Mehat873f2142010-01-14 10:25:07 -0800757 }
San Mehat72759df2010-01-19 13:50:37 -0800758
759 public String[] listTtys() throws IllegalStateException {
760 mContext.enforceCallingOrSelfPermission(
761 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700762 try {
763 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
764 } catch (NativeDaemonConnectorException e) {
765 throw new IllegalStateException(
766 "Unable to communicate to native daemon for listing TTYs");
767 }
San Mehat72759df2010-01-19 13:50:37 -0800768 }
769
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800770 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
771 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800772 try {
773 mContext.enforceCallingOrSelfPermission(
774 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800775 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
Robert Greenwalte5903732011-02-22 16:00:42 -0800776 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
777 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
778 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
779 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
780 } catch (IllegalArgumentException e) {
San Mehat72759df2010-01-19 13:50:37 -0800781 throw new IllegalStateException("Error resolving addr", e);
Kenny Roota80ce062010-06-01 13:23:53 -0700782 } catch (NativeDaemonConnectorException e) {
783 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
San Mehat72759df2010-01-19 13:50:37 -0800784 }
785 }
786
787 public void detachPppd(String tty) throws IllegalStateException {
788 mContext.enforceCallingOrSelfPermission(
789 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700790 try {
791 mConnector.doCommand(String.format("pppd detach %s", tty));
792 } catch (NativeDaemonConnectorException e) {
793 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
794 }
San Mehat72759df2010-01-19 13:50:37 -0800795 }
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800796
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700797 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800798 throws IllegalStateException {
799 mContext.enforceCallingOrSelfPermission(
800 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
801 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700802 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700803 try {
804 mConnector.doCommand(String.format("softap stop " + wlanIface));
805 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
806 mConnector.doCommand(String.format("softap start " + wlanIface));
807 if (wifiConfig == null) {
808 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
809 } else {
810 /**
811 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
812 * argv1 - wlan interface
813 * argv2 - softap interface
814 * argv3 - SSID
815 * argv4 - Security
816 * argv5 - Key
817 * argv6 - Channel
818 * argv7 - Preamble
819 * argv8 - Max SCB
820 */
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800821 String str = String.format("softap set " + wlanIface + " " + softapIface +
822 " %s %s %s", convertQuotedString(wifiConfig.SSID),
823 getSecurityType(wifiConfig),
824 convertQuotedString(wifiConfig.preSharedKey));
Kenny Roota80ce062010-06-01 13:23:53 -0700825 mConnector.doCommand(str);
826 }
827 mConnector.doCommand(String.format("softap startap"));
828 } catch (NativeDaemonConnectorException e) {
829 throw new IllegalStateException("Error communicating to native daemon to start softap", e);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800830 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800831 }
832
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700833 private String convertQuotedString(String s) {
Irfan Sheriff7baec0f2010-05-26 17:16:47 -0700834 if (s == null) {
835 return s;
836 }
837 /* Replace \ with \\, then " with \" and add quotes at end */
838 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700839 }
840
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800841 private String getSecurityType(WifiConfiguration wifiConfig) {
842 switch (wifiConfig.getAuthType()) {
843 case KeyMgmt.WPA_PSK:
844 return "wpa-psk";
845 case KeyMgmt.WPA2_PSK:
846 return "wpa2-psk";
847 default:
848 return "open";
849 }
850 }
851
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800852 public void stopAccessPoint() throws IllegalStateException {
853 mContext.enforceCallingOrSelfPermission(
854 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
855 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700856 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700857 try {
858 mConnector.doCommand("softap stopap");
859 } catch (NativeDaemonConnectorException e) {
860 throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
861 e);
862 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800863 }
864
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700865 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
866 throws IllegalStateException {
867 mContext.enforceCallingOrSelfPermission(
868 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
869 mContext.enforceCallingOrSelfPermission(
870 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700871 try {
872 if (wifiConfig == null) {
873 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
874 } else {
875 String str = String.format("softap set " + wlanIface + " " + softapIface
876 + " %s %s %s", convertQuotedString(wifiConfig.SSID),
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800877 getSecurityType(wifiConfig),
Kenny Roota80ce062010-06-01 13:23:53 -0700878 convertQuotedString(wifiConfig.preSharedKey));
879 mConnector.doCommand(str);
880 }
881 } catch (NativeDaemonConnectorException e) {
882 throw new IllegalStateException("Error communicating to native daemon to set soft AP",
883 e);
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700884 }
885 }
San Mehat91cac642010-03-31 14:31:36 -0700886
887 private long getInterfaceCounter(String iface, boolean rx) {
888 mContext.enforceCallingOrSelfPermission(
889 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
890 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700891 String rsp;
892 try {
893 rsp = mConnector.doCommand(
894 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
895 } catch (NativeDaemonConnectorException e1) {
896 Slog.e(TAG, "Error communicating with native daemon", e1);
897 return -1;
898 }
899
900 String[] tok = rsp.split(" ");
901 if (tok.length < 2) {
902 Slog.e(TAG, String.format("Malformed response for reading %s interface",
903 (rx ? "rx" : "tx")));
904 return -1;
905 }
906
San Mehat91cac642010-03-31 14:31:36 -0700907 int code;
908 try {
909 code = Integer.parseInt(tok[0]);
910 } catch (NumberFormatException nfe) {
911 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
912 return -1;
913 }
914 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
915 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
916 Slog.e(TAG, String.format("Unexpected response code %d", code));
917 return -1;
918 }
919 return Long.parseLong(tok[1]);
920 } catch (Exception e) {
921 Slog.e(TAG, String.format(
922 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
923 }
924 return -1;
925 }
926
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700927 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700928 public NetworkStats getNetworkStatsSummary() {
929 mContext.enforceCallingOrSelfPermission(
930 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
931
932 final String[] ifaces = listInterfaces();
Jeff Sharkey4a971222011-06-11 22:16:55 -0700933 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700934
935 for (String iface : ifaces) {
936 final long rx = getInterfaceCounter(iface, true);
937 final long tx = getInterfaceCounter(iface, false);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700938 stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700939 }
940
Jeff Sharkey4a971222011-06-11 22:16:55 -0700941 return stats;
San Mehat91cac642010-03-31 14:31:36 -0700942 }
943
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700944 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700945 public NetworkStats getNetworkStatsDetail() {
946 mContext.enforceCallingOrSelfPermission(
947 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
948
Jeff Sharkey350083e2011-06-29 10:45:16 -0700949 if (mBandwidthControlEnabled) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700950 return getNetworkStatsDetailNetfilter(UID_ALL);
951 } else {
952 return getNetworkStatsDetailUidstat(UID_ALL);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700953 }
San Mehat91cac642010-03-31 14:31:36 -0700954 }
955
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700956 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700957 public void setInterfaceQuota(String iface, long quota) {
958 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
959
Jeff Sharkey350083e2011-06-29 10:45:16 -0700960 // silently discard when control disabled
961 // TODO: eventually migrate to be always enabled
962 if (!mBandwidthControlEnabled) return;
963
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700964 synchronized (mInterfaceQuota) {
965 if (mInterfaceQuota.contains(iface)) {
966 // TODO: eventually consider throwing
967 return;
968 }
969
970 final StringBuilder command = new StringBuilder();
971 command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
972
973 try {
974 // TODO: add support for quota shared across interfaces
975 mConnector.doCommand(command.toString());
976 mInterfaceQuota.add(iface);
977 } catch (NativeDaemonConnectorException e) {
978 throw new IllegalStateException("Error communicating to native daemon", e);
979 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -0700980 }
981 }
982
983 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700984 public void removeInterfaceQuota(String iface) {
985 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
986
Jeff Sharkey350083e2011-06-29 10:45:16 -0700987 // silently discard when control disabled
988 // TODO: eventually migrate to be always enabled
989 if (!mBandwidthControlEnabled) return;
990
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700991 synchronized (mInterfaceQuota) {
992 if (!mInterfaceQuota.contains(iface)) {
993 // TODO: eventually consider throwing
994 return;
995 }
996
997 final StringBuilder command = new StringBuilder();
998 command.append("bandwidth removeiquota ").append(iface);
999
1000 try {
1001 // TODO: add support for quota shared across interfaces
1002 mConnector.doCommand(command.toString());
1003 mInterfaceQuota.remove(iface);
1004 } catch (NativeDaemonConnectorException e) {
1005 throw new IllegalStateException("Error communicating to native daemon", e);
1006 }
1007 }
1008 }
1009
1010 @Override
1011 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1012 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1013
Jeff Sharkey350083e2011-06-29 10:45:16 -07001014 // silently discard when control disabled
1015 // TODO: eventually migrate to be always enabled
1016 if (!mBandwidthControlEnabled) return;
1017
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001018 synchronized (mUidRejectOnQuota) {
1019 final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1020 if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1021 // TODO: eventually consider throwing
1022 return;
1023 }
1024
1025 final StringBuilder command = new StringBuilder();
1026 command.append("bandwidth");
1027 if (rejectOnQuotaInterfaces) {
1028 command.append(" addnaughtyapps");
1029 } else {
1030 command.append(" removenaughtyapps");
1031 }
1032 command.append(" ").append(uid);
1033
1034 try {
1035 mConnector.doCommand(command.toString());
1036 if (rejectOnQuotaInterfaces) {
1037 mUidRejectOnQuota.put(uid, true);
1038 } else {
1039 mUidRejectOnQuota.delete(uid);
1040 }
1041 } catch (NativeDaemonConnectorException e) {
1042 throw new IllegalStateException("Error communicating to native daemon", e);
1043 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -07001044 }
1045 }
1046
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001047 public NetworkStats getNetworkStatsUidDetail(int uid) {
1048 if (Binder.getCallingUid() != uid) {
1049 mContext.enforceCallingOrSelfPermission(
1050 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1051 }
1052
Jeff Sharkey350083e2011-06-29 10:45:16 -07001053 if (mBandwidthControlEnabled) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001054 return getNetworkStatsDetailNetfilter(uid);
1055 } else {
1056 return getNetworkStatsDetailUidstat(uid);
1057 }
1058 }
1059
1060 /**
1061 * Build {@link NetworkStats} with detailed UID statistics.
1062 */
1063 private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
1064 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001065 final ArrayList<String> keys = Lists.newArrayList();
1066 final ArrayList<String> values = Lists.newArrayList();
1067 final HashMap<String, String> parsed = Maps.newHashMap();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001068
1069 BufferedReader reader = null;
1070 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001071 reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001072
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001073 // parse first line as header
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001074 String line = reader.readLine();
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001075 splitLine(line, keys);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001076
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001077 // parse remaining lines
1078 while ((line = reader.readLine()) != null) {
1079 splitLine(line, values);
1080 parseLine(keys, values, parsed);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001081
1082 try {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001083 final String iface = parsed.get(KEY_IFACE);
Jesse Wilson8568db52011-06-28 19:06:31 -07001084 final int tag = NetworkManagementSocketTagger.kernelToTag(
1085 parsed.get(KEY_TAG_HEX));
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001086 final int uid = Integer.parseInt(parsed.get(KEY_UID));
1087 final long rx = Long.parseLong(parsed.get(KEY_RX));
1088 final long tx = Long.parseLong(parsed.get(KEY_TX));
Jeff Sharkeyd03fd3f2011-06-19 20:55:09 -07001089
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001090 if (limitUid == UID_ALL || limitUid == uid) {
1091 stats.addEntry(iface, uid, tag, rx, tx);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001092 }
1093 } catch (NumberFormatException e) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001094 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001095 }
1096 }
1097 } catch (IOException e) {
1098 Slog.w(TAG, "problem parsing stats: " + e);
1099 } finally {
1100 IoUtils.closeQuietly(reader);
1101 }
1102
Jeff Sharkey4a971222011-06-11 22:16:55 -07001103 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001104 }
1105
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001106 /**
1107 * Build {@link NetworkStats} with detailed UID statistics.
1108 *
1109 * @deprecated since this uses older "uid_stat" data, and doesn't provide
1110 * tag-level granularity or additional variables.
1111 */
1112 @Deprecated
1113 private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
1114 final String[] knownUids;
1115 if (limitUid == UID_ALL) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001116 knownUids = mProcStatsUidstat.list();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001117 } else {
1118 knownUids = new String[] { String.valueOf(limitUid) };
1119 }
1120
1121 final NetworkStats stats = new NetworkStats(
1122 SystemClock.elapsedRealtime(), knownUids.length);
1123 for (String uid : knownUids) {
1124 final int uidInt = Integer.parseInt(uid);
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001125 final File uidPath = new File(mProcStatsUidstat, uid);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001126 final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
1127 final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
1128 stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
1129 }
1130
1131 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001132 }
1133
San Mehatf0db6e12010-04-07 15:22:10 -07001134 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
San Mehat91cac642010-03-31 14:31:36 -07001135 mContext.enforceCallingOrSelfPermission(
1136 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -07001137 try {
1138 mConnector.doCommand(String.format(
1139 "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
1140 } catch (NativeDaemonConnectorException e) {
1141 Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
1142 }
San Mehat91cac642010-03-31 14:31:36 -07001143 }
1144
1145 private int getInterfaceThrottle(String iface, boolean rx) {
1146 mContext.enforceCallingOrSelfPermission(
1147 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1148 try {
Kenny Roota80ce062010-06-01 13:23:53 -07001149 String rsp;
1150 try {
1151 rsp = mConnector.doCommand(
1152 String.format("interface getthrottle %s %s", iface,
1153 (rx ? "rx" : "tx"))).get(0);
1154 } catch (NativeDaemonConnectorException e) {
1155 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
1156 return -1;
1157 }
1158
1159 String[] tok = rsp.split(" ");
1160 if (tok.length < 2) {
1161 Slog.e(TAG, "Malformed response to getthrottle command");
1162 return -1;
1163 }
1164
San Mehat91cac642010-03-31 14:31:36 -07001165 int code;
1166 try {
1167 code = Integer.parseInt(tok[0]);
1168 } catch (NumberFormatException nfe) {
1169 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
1170 return -1;
1171 }
1172 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
1173 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
1174 Slog.e(TAG, String.format("Unexpected response code %d", code));
1175 return -1;
1176 }
1177 return Integer.parseInt(tok[1]);
1178 } catch (Exception e) {
1179 Slog.e(TAG, String.format(
1180 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
1181 }
1182 return -1;
1183 }
1184
1185 public int getInterfaceRxThrottle(String iface) {
1186 return getInterfaceThrottle(iface, true);
1187 }
1188
1189 public int getInterfaceTxThrottle(String iface) {
1190 return getInterfaceThrottle(iface, false);
1191 }
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001192
1193 /**
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001194 * Split given line into {@link ArrayList}.
1195 */
1196 private static void splitLine(String line, ArrayList<String> outSplit) {
1197 outSplit.clear();
1198
1199 final StringTokenizer t = new StringTokenizer(line);
1200 while (t.hasMoreTokens()) {
1201 outSplit.add(t.nextToken());
1202 }
1203 }
1204
1205 /**
1206 * Zip the two given {@link ArrayList} as key and value pairs into
1207 * {@link HashMap}.
1208 */
1209 private static void parseLine(
1210 ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
1211 outParsed.clear();
1212
1213 final int size = Math.min(keys.size(), values.size());
1214 for (int i = 0; i < size; i++) {
1215 outParsed.put(keys.get(i), values.get(i));
1216 }
1217 }
1218
1219 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001220 * Utility method to read a single plain-text {@link Long} from the given
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001221 * {@link File}, usually from a {@code /proc/} filesystem.
1222 */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001223 private static long readSingleLongFromFile(File file) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001224 try {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001225 final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
1226 return Long.parseLong(new String(buffer).trim());
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001227 } catch (NumberFormatException e) {
1228 return -1;
1229 } catch (IOException e) {
1230 return -1;
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001231 }
1232 }
San Mehat873f2142010-01-14 10:25:07 -08001233}