blob: 2366fcb41c8d0f06911551e3178a65e93729a082 [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;
San Mehat873f2142010-01-14 10:25:07 -080057import java.net.UnknownHostException;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070058import java.util.ArrayList;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070059import java.util.HashMap;
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -070060import java.util.HashSet;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070061import java.util.NoSuchElementException;
62import java.util.StringTokenizer;
Robert Greenwalte5c3afb2010-09-22 14:32:35 -070063import java.util.concurrent.CountDownLatch;
San Mehat873f2142010-01-14 10:25:07 -080064
Jeff Sharkey9a13f362011-04-26 16:25:36 -070065import libcore.io.IoUtils;
66
San Mehat873f2142010-01-14 10:25:07 -080067/**
68 * @hide
69 */
70class NetworkManagementService extends INetworkManagementService.Stub {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070071 private static final String TAG = "NetworkManagementService";
Dianne Hackborncef65ee2010-09-30 18:27:22 -070072 private static final boolean DBG = false;
Kenny Root305bcbf2010-09-03 07:56:38 -070073 private static final String NETD_TAG = "NetdConnector";
74
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -070075 private static final int ADD = 1;
76 private static final int REMOVE = 2;
77
Jeff Sharkey4414cea2011-06-24 17:05:24 -070078 /** Path to {@code /proc/uid_stat}. */
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070079 @Deprecated
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070080 private final File mStatsUid;
81 /** Path to {@code /proc/net/dev}. */
82 private final File mStatsIface;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070083 /** Path to {@code /proc/net/xt_qtaguid/stats}. */
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070084 private final File mStatsXtUid;
85 /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */
86 private final File mStatsXtIface;
Jeff Sharkey4414cea2011-06-24 17:05:24 -070087
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -070088 /** {@link #mStatsXtUid} headers. */
Jeff Sharkey4414cea2011-06-24 17:05:24 -070089 private static final String KEY_IFACE = "iface";
90 private static final String KEY_TAG_HEX = "acct_tag_hex";
91 private static final String KEY_UID = "uid_tag_int";
92 private static final String KEY_RX = "rx_bytes";
93 private static final String KEY_TX = "tx_bytes";
Jeff Sharkeyeedcb952011-05-17 14:55:15 -070094
San Mehat873f2142010-01-14 10:25:07 -080095 class NetdResponseCode {
JP Abgrall12b933d2011-07-14 18:09:22 -070096 /* Keep in sync with system/netd/ResponseCode.h */
San Mehat873f2142010-01-14 10:25:07 -080097 public static final int InterfaceListResult = 110;
98 public static final int TetherInterfaceListResult = 111;
99 public static final int TetherDnsFwdTgtListResult = 112;
San Mehat72759df2010-01-19 13:50:37 -0800100 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -0800101
102 public static final int TetherStatusResult = 210;
103 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -0800104 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -0800105 public static final int SoftapStatusResult = 214;
San Mehat91cac642010-03-31 14:31:36 -0700106 public static final int InterfaceRxCounterResult = 216;
107 public static final int InterfaceTxCounterResult = 217;
108 public static final int InterfaceRxThrottleResult = 218;
109 public static final int InterfaceTxThrottleResult = 219;
Robert Greenwalte3253922010-02-18 09:23:25 -0800110
111 public static final int InterfaceChange = 600;
JP Abgrall12b933d2011-07-14 18:09:22 -0700112 public static final int BandwidthControl = 601;
San Mehat873f2142010-01-14 10:25:07 -0800113 }
114
115 /**
116 * Binder context for this service
117 */
118 private Context mContext;
119
120 /**
121 * connector object for communicating with netd
122 */
123 private NativeDaemonConnector mConnector;
124
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700125 private Thread mThread;
126 private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
127
San Mehat4d02d002010-01-22 16:07:46 -0800128 private ArrayList<INetworkManagementEventObserver> mObservers;
129
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -0700130 /** Set of interfaces with active quotas. */
131 private HashSet<String> mInterfaceQuota = Sets.newHashSet();
132 /** Set of UIDs with active reject rules. */
133 private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
134
Jeff Sharkey350083e2011-06-29 10:45:16 -0700135 private boolean mBandwidthControlEnabled;
136
San Mehat873f2142010-01-14 10:25:07 -0800137 /**
138 * Constructs a new NetworkManagementService instance
139 *
140 * @param context Binder context for this service
141 */
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700142 private NetworkManagementService(Context context, File procRoot) {
San Mehat873f2142010-01-14 10:25:07 -0800143 mContext = context;
San Mehat4d02d002010-01-22 16:07:46 -0800144 mObservers = new ArrayList<INetworkManagementEventObserver>();
145
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700146 mStatsUid = new File(procRoot, "uid_stat");
147 mStatsIface = new File(procRoot, "net/dev");
148 mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
149 mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat");
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700150
Marco Nelissen62dbb222010-02-18 10:56:30 -0800151 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
152 return;
153 }
154
San Mehat873f2142010-01-14 10:25:07 -0800155 mConnector = new NativeDaemonConnector(
Kenny Root305bcbf2010-09-03 07:56:38 -0700156 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700157 mThread = new Thread(mConnector, NETD_TAG);
158 }
159
160 public static NetworkManagementService create(Context context) throws InterruptedException {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700161 NetworkManagementService service = new NetworkManagementService(
162 context, new File("/proc/"));
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700163 if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
164 service.mThread.start();
165 if (DBG) Slog.d(TAG, "Awaiting socket connection");
166 service.mConnectedSignal.await();
167 if (DBG) Slog.d(TAG, "Connected");
168 return service;
San Mehat873f2142010-01-14 10:25:07 -0800169 }
170
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700171 // @VisibleForTesting
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700172 public static NetworkManagementService createForTest(
173 Context context, File procRoot, boolean bandwidthControlEnabled) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700174 // TODO: eventually connect with mock netd
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700175 final NetworkManagementService service = new NetworkManagementService(context, procRoot);
176 service.mBandwidthControlEnabled = bandwidthControlEnabled;
177 return service;
Jeff Sharkey4414cea2011-06-24 17:05:24 -0700178 }
179
Jeff Sharkey350083e2011-06-29 10:45:16 -0700180 public void systemReady() {
181
182 // only enable bandwidth control when support exists, and requested by
183 // system setting.
184 // TODO: eventually migrate to be always enabled
185 final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
186 final boolean shouldEnable =
187 Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 0) != 0;
188
189 mBandwidthControlEnabled = false;
190 if (hasKernelSupport && shouldEnable) {
191 Slog.d(TAG, "enabling bandwidth control");
192 try {
193 mConnector.doCommand("bandwidth enable");
194 mBandwidthControlEnabled = true;
195 } catch (NativeDaemonConnectorException e) {
196 Slog.e(TAG, "problem enabling bandwidth controls", e);
197 }
198 } else {
199 Slog.d(TAG, "not enabling bandwidth control");
200 }
Jeff Sharkey62a2c8f2011-07-13 15:24:02 -0700201
202 SystemProperties.set(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
203 mBandwidthControlEnabled ? "1" : "0");
Jeff Sharkey350083e2011-06-29 10:45:16 -0700204 }
205
San Mehat4d02d002010-01-22 16:07:46 -0800206 public void registerObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800207 Slog.d(TAG, "Registering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800208 mObservers.add(obs);
209 }
210
211 public void unregisterObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800212 Slog.d(TAG, "Unregistering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800213 mObservers.remove(mObservers.indexOf(obs));
214 }
215
216 /**
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700217 * Notify our observers of an interface status change
San Mehat4d02d002010-01-22 16:07:46 -0800218 */
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700219 private void notifyInterfaceStatusChanged(String iface, boolean up) {
San Mehat4d02d002010-01-22 16:07:46 -0800220 for (INetworkManagementEventObserver obs : mObservers) {
221 try {
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700222 obs.interfaceStatusChanged(iface, up);
223 } catch (Exception ex) {
224 Slog.w(TAG, "Observer notifier failed", ex);
225 }
226 }
227 }
228
229 /**
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700230 * Notify our observers of an interface link state change
Mike J. Chen6143f5f2011-06-23 15:17:51 -0700231 * (typically, an Ethernet cable has been plugged-in or unplugged).
232 */
233 private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
234 for (INetworkManagementEventObserver obs : mObservers) {
235 try {
236 obs.interfaceLinkStateChanged(iface, up);
San Mehat4d02d002010-01-22 16:07:46 -0800237 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800238 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800239 }
240 }
241 }
242
243 /**
244 * Notify our observers of an interface addition.
245 */
246 private void notifyInterfaceAdded(String iface) {
247 for (INetworkManagementEventObserver obs : mObservers) {
248 try {
249 obs.interfaceAdded(iface);
250 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800251 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800252 }
253 }
254 }
255
256 /**
257 * Notify our observers of an interface removal.
258 */
259 private void notifyInterfaceRemoved(String iface) {
260 for (INetworkManagementEventObserver obs : mObservers) {
261 try {
262 obs.interfaceRemoved(iface);
263 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800264 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800265 }
266 }
267 }
268
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700269 /**
JP Abgrall12b933d2011-07-14 18:09:22 -0700270 * Notify our observers of a limit reached.
271 */
272 private void notifyLimitReached(String limitName, String iface) {
273 for (INetworkManagementEventObserver obs : mObservers) {
274 try {
275 obs.limitReached(limitName, iface);
276 Slog.d(TAG, "Observer notified limit reached for " + limitName + " " + iface);
277 } catch (Exception ex) {
278 Slog.w(TAG, "Observer notifier failed", ex);
279 }
280 }
281 }
282
283 /**
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700284 * Let us know the daemon is connected
285 */
286 protected void onConnected() {
287 if (DBG) Slog.d(TAG, "onConnected");
288 mConnectedSignal.countDown();
289 }
290
San Mehat4d02d002010-01-22 16:07:46 -0800291
San Mehat873f2142010-01-14 10:25:07 -0800292 //
293 // Netd Callback handling
294 //
295
296 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
297 public void onDaemonConnected() {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700298 NetworkManagementService.this.onConnected();
San Mehat873f2142010-01-14 10:25:07 -0800299 new Thread() {
300 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800301 }
302 }.start();
303 }
304 public boolean onEvent(int code, String raw, String[] cooked) {
JP Abgrall12b933d2011-07-14 18:09:22 -0700305 switch (code) {
306 case NetdResponseCode.InterfaceChange:
307 /*
308 * a network interface change occured
309 * Format: "NNN Iface added <name>"
310 * "NNN Iface removed <name>"
311 * "NNN Iface changed <name> <up/down>"
312 * "NNN Iface linkstatus <name> <up/down>"
313 */
314 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
315 throw new IllegalStateException(
316 String.format("Invalid event from daemon (%s)", raw));
317 }
318 if (cooked[2].equals("added")) {
319 notifyInterfaceAdded(cooked[3]);
320 return true;
321 } else if (cooked[2].equals("removed")) {
322 notifyInterfaceRemoved(cooked[3]);
323 return true;
324 } else if (cooked[2].equals("changed") && cooked.length == 5) {
325 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
326 return true;
327 } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
328 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
329 return true;
330 }
Robert Greenwalte3253922010-02-18 09:23:25 -0800331 throw new IllegalStateException(
332 String.format("Invalid event from daemon (%s)", raw));
JP Abgrall12b933d2011-07-14 18:09:22 -0700333 // break;
334 case NetdResponseCode.BandwidthControl:
335 /*
336 * Bandwidth control needs some attention
337 * Format: "NNN limit alert <alertName> <ifaceName>"
338 */
339 if (cooked.length < 5 || !cooked[1].equals("limit")) {
340 throw new IllegalStateException(
341 String.format("Invalid event from daemon (%s)", raw));
342 }
343 if (cooked[2].equals("alert")) {
344 notifyLimitReached(cooked[3], cooked[4]);
345 return true;
346 }
347 throw new IllegalStateException(
348 String.format("Invalid event from daemon (%s)", raw));
349 // break;
350 default: break;
Robert Greenwalte3253922010-02-18 09:23:25 -0800351 }
352 return false;
San Mehat873f2142010-01-14 10:25:07 -0800353 }
354 }
355
San Mehated4fc8a2010-01-22 12:28:36 -0800356
San Mehat873f2142010-01-14 10:25:07 -0800357 //
358 // INetworkManagementService members
359 //
360
361 public String[] listInterfaces() throws IllegalStateException {
362 mContext.enforceCallingOrSelfPermission(
363 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
364
Kenny Roota80ce062010-06-01 13:23:53 -0700365 try {
366 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
367 } catch (NativeDaemonConnectorException e) {
368 throw new IllegalStateException(
369 "Cannot communicate with native daemon to list interfaces");
370 }
San Mehated4fc8a2010-01-22 12:28:36 -0800371 }
372
373 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
Kenny Roota80ce062010-06-01 13:23:53 -0700374 String rsp;
375 try {
376 rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
377 } catch (NativeDaemonConnectorException e) {
378 throw new IllegalStateException(
379 "Cannot communicate with native daemon to get interface config");
380 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800381 Slog.d(TAG, String.format("rsp <%s>", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800382
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800383 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3]
San Mehated4fc8a2010-01-22 12:28:36 -0800384 StringTokenizer st = new StringTokenizer(rsp);
385
Kenny Roota80ce062010-06-01 13:23:53 -0700386 InterfaceConfiguration cfg;
San Mehated4fc8a2010-01-22 12:28:36 -0800387 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700388 try {
389 int code = Integer.parseInt(st.nextToken(" "));
390 if (code != NetdResponseCode.InterfaceGetCfgResult) {
391 throw new IllegalStateException(
392 String.format("Expected code %d, but got %d",
393 NetdResponseCode.InterfaceGetCfgResult, code));
394 }
395 } catch (NumberFormatException nfe) {
San Mehated4fc8a2010-01-22 12:28:36 -0800396 throw new IllegalStateException(
Kenny Roota80ce062010-06-01 13:23:53 -0700397 String.format("Invalid response from daemon (%s)", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800398 }
Kenny Roota80ce062010-06-01 13:23:53 -0700399
400 cfg = new InterfaceConfiguration();
401 cfg.hwAddr = st.nextToken(" ");
Robert Greenwalted126402011-01-28 15:34:55 -0800402 InetAddress addr = null;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800403 int prefixLength = 0;
Kenny Roota80ce062010-06-01 13:23:53 -0700404 try {
Robert Greenwalte5903732011-02-22 16:00:42 -0800405 addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
406 } catch (IllegalArgumentException iae) {
407 Slog.e(TAG, "Failed to parse ipaddr", iae);
Kenny Roota80ce062010-06-01 13:23:53 -0700408 }
409
410 try {
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800411 prefixLength = Integer.parseInt(st.nextToken(" "));
412 } catch (NumberFormatException nfe) {
413 Slog.e(TAG, "Failed to parse prefixLength", nfe);
Kenny Roota80ce062010-06-01 13:23:53 -0700414 }
Robert Greenwalt04808c22010-12-13 17:01:41 -0800415
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800416 cfg.addr = new LinkAddress(addr, prefixLength);
Kenny Roota80ce062010-06-01 13:23:53 -0700417 cfg.interfaceFlags = st.nextToken("]").trim() +"]";
418 } catch (NoSuchElementException nsee) {
San Mehated4fc8a2010-01-22 12:28:36 -0800419 throw new IllegalStateException(
420 String.format("Invalid response from daemon (%s)", rsp));
421 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800422 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
San Mehated4fc8a2010-01-22 12:28:36 -0800423 return cfg;
424 }
425
426 public void setInterfaceConfig(
427 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
Robert Greenwalted126402011-01-28 15:34:55 -0800428 LinkAddress linkAddr = cfg.addr;
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800429 if (linkAddr == null || linkAddr.getAddress() == null) {
430 throw new IllegalStateException("Null LinkAddress given");
Robert Greenwalted126402011-01-28 15:34:55 -0800431 }
Robert Greenwalt2d2afd12011-02-01 15:30:46 -0800432 String cmd = String.format("interface setcfg %s %s %d %s", iface,
433 linkAddr.getAddress().getHostAddress(),
434 linkAddr.getNetworkPrefixLength(),
435 cfg.interfaceFlags);
Kenny Roota80ce062010-06-01 13:23:53 -0700436 try {
437 mConnector.doCommand(cmd);
438 } catch (NativeDaemonConnectorException e) {
439 throw new IllegalStateException(
Robert Greenwalt81d5ad52010-12-16 11:27:38 -0800440 "Unable to communicate with native daemon to interface setcfg - " + e);
Kenny Roota80ce062010-06-01 13:23:53 -0700441 }
San Mehat873f2142010-01-14 10:25:07 -0800442 }
443
Irfan Sherifff5600612011-06-16 10:26:28 -0700444 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
445 IPv6 addresses on interface down, but we need to do full clean up here */
446 public void clearInterfaceAddresses(String iface) throws IllegalStateException {
447 String cmd = String.format("interface clearaddrs %s", iface);
448 try {
449 mConnector.doCommand(cmd);
450 } catch (NativeDaemonConnectorException e) {
451 throw new IllegalStateException(
452 "Unable to communicate with native daemon to interface clearallips - " + e);
453 }
454 }
455
Robert Greenwalt59b1a4e2011-05-10 15:05:02 -0700456 public void addRoute(String interfaceName, RouteInfo route) {
457 modifyRoute(interfaceName, ADD, route);
458 }
459
460 public void removeRoute(String interfaceName, RouteInfo route) {
461 modifyRoute(interfaceName, REMOVE, route);
462 }
463
464 private void modifyRoute(String interfaceName, int action, RouteInfo route) {
465 ArrayList<String> rsp;
466
467 StringBuilder cmd;
468
469 switch (action) {
470 case ADD:
471 {
472 cmd = new StringBuilder("interface route add " + interfaceName);
473 break;
474 }
475 case REMOVE:
476 {
477 cmd = new StringBuilder("interface route remove " + interfaceName);
478 break;
479 }
480 default:
481 throw new IllegalStateException("Unknown action type " + action);
482 }
483
484 // create triplet: dest-ip-addr prefixlength gateway-ip-addr
485 LinkAddress la = route.getDestination();
486 cmd.append(' ');
487 cmd.append(la.getAddress().getHostAddress());
488 cmd.append(' ');
489 cmd.append(la.getNetworkPrefixLength());
490 cmd.append(' ');
491 if (route.getGateway() == null) {
492 if (la.getAddress() instanceof Inet4Address) {
493 cmd.append("0.0.0.0");
494 } else {
495 cmd.append ("::0");
496 }
497 } else {
498 cmd.append(route.getGateway().getHostAddress());
499 }
500 try {
501 rsp = mConnector.doCommand(cmd.toString());
502 } catch (NativeDaemonConnectorException e) {
503 throw new IllegalStateException(
504 "Unable to communicate with native dameon to add routes - "
505 + e);
506 }
507
508 for (String line : rsp) {
509 Log.v(TAG, "add route response is " + line);
510 }
511 }
512
513 private ArrayList<String> readRouteList(String filename) {
514 FileInputStream fstream = null;
515 ArrayList<String> list = new ArrayList<String>();
516
517 try {
518 fstream = new FileInputStream(filename);
519 DataInputStream in = new DataInputStream(fstream);
520 BufferedReader br = new BufferedReader(new InputStreamReader(in));
521 String s;
522
523 // throw away the title line
524
525 while (((s = br.readLine()) != null) && (s.length() != 0)) {
526 list.add(s);
527 }
528 } catch (IOException ex) {
529 // return current list, possibly empty
530 } finally {
531 if (fstream != null) {
532 try {
533 fstream.close();
534 } catch (IOException ex) {}
535 }
536 }
537
538 return list;
539 }
540
541 public RouteInfo[] getRoutes(String interfaceName) {
542 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
543
544 // v4 routes listed as:
545 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
546 for (String s : readRouteList("/proc/net/route")) {
547 String[] fields = s.split("\t");
548
549 if (fields.length > 7) {
550 String iface = fields[0];
551
552 if (interfaceName.equals(iface)) {
553 String dest = fields[1];
554 String gate = fields[2];
555 String flags = fields[3]; // future use?
556 String mask = fields[7];
557 try {
558 // address stored as a hex string, ex: 0014A8C0
559 InetAddress destAddr =
560 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
561 int prefixLength =
562 NetworkUtils.netmaskIntToPrefixLength(
563 (int)Long.parseLong(mask, 16));
564 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
565
566 // address stored as a hex string, ex 0014A8C0
567 InetAddress gatewayAddr =
568 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
569
570 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
571 routes.add(route);
572 } catch (Exception e) {
573 Log.e(TAG, "Error parsing route " + s + " : " + e);
574 continue;
575 }
576 }
577 }
578 }
579
580 // v6 routes listed as:
581 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
582 for (String s : readRouteList("/proc/net/ipv6_route")) {
583 String[]fields = s.split("\\s+");
584 if (fields.length > 9) {
585 String iface = fields[9].trim();
586 if (interfaceName.equals(iface)) {
587 String dest = fields[0];
588 String prefix = fields[1];
589 String gate = fields[4];
590
591 try {
592 // prefix length stored as a hex string, ex 40
593 int prefixLength = Integer.parseInt(prefix, 16);
594
595 // address stored as a 32 char hex string
596 // ex fe800000000000000000000000000000
597 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
598 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
599
600 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
601
602 RouteInfo route = new RouteInfo(linkAddress, gateAddr);
603 routes.add(route);
604 } catch (Exception e) {
605 Log.e(TAG, "Error parsing route " + s + " : " + e);
606 continue;
607 }
608 }
609 }
610 }
611 return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
612 }
613
San Mehat873f2142010-01-14 10:25:07 -0800614 public void shutdown() {
615 if (mContext.checkCallingOrSelfPermission(
616 android.Manifest.permission.SHUTDOWN)
617 != PackageManager.PERMISSION_GRANTED) {
618 throw new SecurityException("Requires SHUTDOWN permission");
619 }
620
Joe Onorato8a9b2202010-02-26 18:56:32 -0800621 Slog.d(TAG, "Shutting down");
San Mehat873f2142010-01-14 10:25:07 -0800622 }
623
624 public boolean getIpForwardingEnabled() throws IllegalStateException{
625 mContext.enforceCallingOrSelfPermission(
626 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
627
Kenny Roota80ce062010-06-01 13:23:53 -0700628 ArrayList<String> rsp;
629 try {
630 rsp = mConnector.doCommand("ipfwd status");
631 } catch (NativeDaemonConnectorException e) {
632 throw new IllegalStateException(
633 "Unable to communicate with native daemon to ipfwd status");
634 }
San Mehat873f2142010-01-14 10:25:07 -0800635
636 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700637 String[] tok = line.split(" ");
638 if (tok.length < 3) {
639 Slog.e(TAG, "Malformed response from native daemon: " + line);
640 return false;
641 }
642
San Mehat873f2142010-01-14 10:25:07 -0800643 int code = Integer.parseInt(tok[0]);
644 if (code == NetdResponseCode.IpFwdStatusResult) {
645 // 211 Forwarding <enabled/disabled>
Kenny Roota80ce062010-06-01 13:23:53 -0700646 return "enabled".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800647 } else {
648 throw new IllegalStateException(String.format("Unexpected response code %d", code));
649 }
650 }
651 throw new IllegalStateException("Got an empty response");
652 }
653
654 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
655 mContext.enforceCallingOrSelfPermission(
656 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
657 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
658 }
659
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700660 public void startTethering(String[] dhcpRange)
San Mehat873f2142010-01-14 10:25:07 -0800661 throws IllegalStateException {
662 mContext.enforceCallingOrSelfPermission(
663 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltbfb7bfa2010-03-24 16:03:21 -0700664 // cmd is "tether start first_start first_stop second_start second_stop ..."
665 // an odd number of addrs will fail
666 String cmd = "tether start";
667 for (String d : dhcpRange) {
668 cmd += " " + d;
669 }
Kenny Roota80ce062010-06-01 13:23:53 -0700670
671 try {
672 mConnector.doCommand(cmd);
673 } catch (NativeDaemonConnectorException e) {
674 throw new IllegalStateException("Unable to communicate to native daemon");
675 }
San Mehat873f2142010-01-14 10:25:07 -0800676 }
677
678 public void stopTethering() throws IllegalStateException {
679 mContext.enforceCallingOrSelfPermission(
680 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700681 try {
682 mConnector.doCommand("tether stop");
683 } catch (NativeDaemonConnectorException e) {
684 throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
685 }
San Mehat873f2142010-01-14 10:25:07 -0800686 }
687
688 public boolean isTetheringStarted() throws IllegalStateException {
689 mContext.enforceCallingOrSelfPermission(
690 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
691
Kenny Roota80ce062010-06-01 13:23:53 -0700692 ArrayList<String> rsp;
693 try {
694 rsp = mConnector.doCommand("tether status");
695 } catch (NativeDaemonConnectorException e) {
696 throw new IllegalStateException(
697 "Unable to communicate to native daemon to get tether status");
698 }
San Mehat873f2142010-01-14 10:25:07 -0800699
700 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700701 String[] tok = line.split(" ");
702 if (tok.length < 3) {
703 throw new IllegalStateException("Malformed response for tether status: " + line);
704 }
San Mehat873f2142010-01-14 10:25:07 -0800705 int code = Integer.parseInt(tok[0]);
706 if (code == NetdResponseCode.TetherStatusResult) {
707 // XXX: Tethering services <started/stopped> <TBD>...
Kenny Roota80ce062010-06-01 13:23:53 -0700708 return "started".equals(tok[2]);
San Mehat873f2142010-01-14 10:25:07 -0800709 } else {
710 throw new IllegalStateException(String.format("Unexpected response code %d", code));
711 }
712 }
713 throw new IllegalStateException("Got an empty response");
714 }
715
716 public void tetherInterface(String iface) throws IllegalStateException {
717 mContext.enforceCallingOrSelfPermission(
718 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700719 try {
720 mConnector.doCommand("tether interface add " + iface);
721 } catch (NativeDaemonConnectorException e) {
722 throw new IllegalStateException(
723 "Unable to communicate to native daemon for adding tether interface");
724 }
San Mehat873f2142010-01-14 10:25:07 -0800725 }
726
727 public void untetherInterface(String iface) {
728 mContext.enforceCallingOrSelfPermission(
729 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700730 try {
731 mConnector.doCommand("tether interface remove " + iface);
732 } catch (NativeDaemonConnectorException e) {
733 throw new IllegalStateException(
734 "Unable to communicate to native daemon for removing tether interface");
735 }
San Mehat873f2142010-01-14 10:25:07 -0800736 }
737
738 public String[] listTetheredInterfaces() throws IllegalStateException {
739 mContext.enforceCallingOrSelfPermission(
740 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700741 try {
742 return mConnector.doListCommand(
743 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
744 } catch (NativeDaemonConnectorException e) {
745 throw new IllegalStateException(
746 "Unable to communicate to native daemon for listing tether interfaces");
747 }
San Mehat873f2142010-01-14 10:25:07 -0800748 }
749
750 public void setDnsForwarders(String[] dns) throws IllegalStateException {
751 mContext.enforceCallingOrSelfPermission(
752 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
753 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800754 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800755 for (String s : dns) {
Robert Greenwalte5903732011-02-22 16:00:42 -0800756 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800757 }
Kenny Roota80ce062010-06-01 13:23:53 -0700758 try {
759 mConnector.doCommand(cmd);
760 } catch (NativeDaemonConnectorException e) {
761 throw new IllegalStateException(
762 "Unable to communicate to native daemon for setting tether dns");
763 }
Robert Greenwalte5903732011-02-22 16:00:42 -0800764 } catch (IllegalArgumentException e) {
San Mehat873f2142010-01-14 10:25:07 -0800765 throw new IllegalStateException("Error resolving dns name", e);
766 }
767 }
768
769 public String[] getDnsForwarders() throws IllegalStateException {
770 mContext.enforceCallingOrSelfPermission(
771 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700772 try {
773 return mConnector.doListCommand(
774 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
775 } catch (NativeDaemonConnectorException e) {
776 throw new IllegalStateException(
777 "Unable to communicate to native daemon for listing tether dns");
778 }
San Mehat873f2142010-01-14 10:25:07 -0800779 }
780
781 public void enableNat(String internalInterface, String externalInterface)
782 throws IllegalStateException {
783 mContext.enforceCallingOrSelfPermission(
784 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700785 try {
786 mConnector.doCommand(
787 String.format("nat enable %s %s", internalInterface, externalInterface));
788 } catch (NativeDaemonConnectorException e) {
789 throw new IllegalStateException(
790 "Unable to communicate to native daemon for enabling NAT interface");
791 }
San Mehat873f2142010-01-14 10:25:07 -0800792 }
793
794 public void disableNat(String internalInterface, String externalInterface)
795 throws IllegalStateException {
796 mContext.enforceCallingOrSelfPermission(
797 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700798 try {
799 mConnector.doCommand(
800 String.format("nat disable %s %s", internalInterface, externalInterface));
801 } catch (NativeDaemonConnectorException e) {
802 throw new IllegalStateException(
803 "Unable to communicate to native daemon for disabling NAT interface");
804 }
San Mehat873f2142010-01-14 10:25:07 -0800805 }
San Mehat72759df2010-01-19 13:50:37 -0800806
807 public String[] listTtys() throws IllegalStateException {
808 mContext.enforceCallingOrSelfPermission(
809 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700810 try {
811 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
812 } catch (NativeDaemonConnectorException e) {
813 throw new IllegalStateException(
814 "Unable to communicate to native daemon for listing TTYs");
815 }
San Mehat72759df2010-01-19 13:50:37 -0800816 }
817
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800818 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
819 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800820 try {
821 mContext.enforceCallingOrSelfPermission(
822 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800823 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
Robert Greenwalte5903732011-02-22 16:00:42 -0800824 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
825 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
826 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
827 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
828 } catch (IllegalArgumentException e) {
San Mehat72759df2010-01-19 13:50:37 -0800829 throw new IllegalStateException("Error resolving addr", e);
Kenny Roota80ce062010-06-01 13:23:53 -0700830 } catch (NativeDaemonConnectorException e) {
831 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
San Mehat72759df2010-01-19 13:50:37 -0800832 }
833 }
834
835 public void detachPppd(String tty) throws IllegalStateException {
836 mContext.enforceCallingOrSelfPermission(
837 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700838 try {
839 mConnector.doCommand(String.format("pppd detach %s", tty));
840 } catch (NativeDaemonConnectorException e) {
841 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
842 }
San Mehat72759df2010-01-19 13:50:37 -0800843 }
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800844
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700845 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800846 throws IllegalStateException {
847 mContext.enforceCallingOrSelfPermission(
848 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
849 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700850 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700851 try {
852 mConnector.doCommand(String.format("softap stop " + wlanIface));
853 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
854 mConnector.doCommand(String.format("softap start " + wlanIface));
855 if (wifiConfig == null) {
856 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
857 } else {
858 /**
859 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
860 * argv1 - wlan interface
861 * argv2 - softap interface
862 * argv3 - SSID
863 * argv4 - Security
864 * argv5 - Key
865 * argv6 - Channel
866 * argv7 - Preamble
867 * argv8 - Max SCB
868 */
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800869 String str = String.format("softap set " + wlanIface + " " + softapIface +
870 " %s %s %s", convertQuotedString(wifiConfig.SSID),
871 getSecurityType(wifiConfig),
872 convertQuotedString(wifiConfig.preSharedKey));
Kenny Roota80ce062010-06-01 13:23:53 -0700873 mConnector.doCommand(str);
874 }
875 mConnector.doCommand(String.format("softap startap"));
876 } catch (NativeDaemonConnectorException e) {
877 throw new IllegalStateException("Error communicating to native daemon to start softap", e);
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800878 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800879 }
880
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700881 private String convertQuotedString(String s) {
Irfan Sheriff7baec0f2010-05-26 17:16:47 -0700882 if (s == null) {
883 return s;
884 }
885 /* Replace \ with \\, then " with \" and add quotes at end */
886 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
Irfan Sheriffa6e559e2010-05-24 14:55:42 -0700887 }
888
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800889 private String getSecurityType(WifiConfiguration wifiConfig) {
890 switch (wifiConfig.getAuthType()) {
891 case KeyMgmt.WPA_PSK:
892 return "wpa-psk";
893 case KeyMgmt.WPA2_PSK:
894 return "wpa2-psk";
895 default:
896 return "open";
897 }
898 }
899
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800900 public void stopAccessPoint() throws IllegalStateException {
901 mContext.enforceCallingOrSelfPermission(
902 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
903 mContext.enforceCallingOrSelfPermission(
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700904 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700905 try {
906 mConnector.doCommand("softap stopap");
907 } catch (NativeDaemonConnectorException e) {
908 throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
909 e);
910 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800911 }
912
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700913 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
914 throws IllegalStateException {
915 mContext.enforceCallingOrSelfPermission(
916 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
917 mContext.enforceCallingOrSelfPermission(
918 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -0700919 try {
920 if (wifiConfig == null) {
921 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
922 } else {
923 String str = String.format("softap set " + wlanIface + " " + softapIface
924 + " %s %s %s", convertQuotedString(wifiConfig.SSID),
Irfan Sheriffec8d23a2011-02-16 17:00:33 -0800925 getSecurityType(wifiConfig),
Kenny Roota80ce062010-06-01 13:23:53 -0700926 convertQuotedString(wifiConfig.preSharedKey));
927 mConnector.doCommand(str);
928 }
929 } catch (NativeDaemonConnectorException e) {
930 throw new IllegalStateException("Error communicating to native daemon to set soft AP",
931 e);
Irfan Sheriffc2f54c22010-03-18 14:02:22 -0700932 }
933 }
San Mehat91cac642010-03-31 14:31:36 -0700934
935 private long getInterfaceCounter(String iface, boolean rx) {
936 mContext.enforceCallingOrSelfPermission(
937 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
938 try {
Kenny Roota80ce062010-06-01 13:23:53 -0700939 String rsp;
940 try {
941 rsp = mConnector.doCommand(
942 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
943 } catch (NativeDaemonConnectorException e1) {
944 Slog.e(TAG, "Error communicating with native daemon", e1);
945 return -1;
946 }
947
948 String[] tok = rsp.split(" ");
949 if (tok.length < 2) {
950 Slog.e(TAG, String.format("Malformed response for reading %s interface",
951 (rx ? "rx" : "tx")));
952 return -1;
953 }
954
San Mehat91cac642010-03-31 14:31:36 -0700955 int code;
956 try {
957 code = Integer.parseInt(tok[0]);
958 } catch (NumberFormatException nfe) {
959 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
960 return -1;
961 }
962 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
963 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
964 Slog.e(TAG, String.format("Unexpected response code %d", code));
965 return -1;
966 }
967 return Long.parseLong(tok[1]);
968 } catch (Exception e) {
969 Slog.e(TAG, String.format(
970 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
971 }
972 return -1;
973 }
974
Jeff Sharkeyeedcb952011-05-17 14:55:15 -0700975 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700976 public NetworkStats getNetworkStatsSummary() {
977 mContext.enforceCallingOrSelfPermission(
978 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
979
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700980 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
981 final NetworkStats.Entry entry = new NetworkStats.Entry();
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700982
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -0700983 final HashSet<String> activeIfaces = Sets.newHashSet();
984 final ArrayList<String> values = Lists.newArrayList();
985
986 BufferedReader reader = null;
987 try {
988 reader = new BufferedReader(new FileReader(mStatsIface));
989
990 // skip first two header lines
991 reader.readLine();
992 reader.readLine();
993
994 // parse remaining lines
995 String line;
996 while ((line = reader.readLine()) != null) {
997 splitLine(line, values);
998
999 try {
1000 entry.iface = values.get(0);
1001 entry.uid = UID_ALL;
1002 entry.tag = TAG_NONE;
1003 entry.rxBytes = Long.parseLong(values.get(1));
1004 entry.rxPackets = Long.parseLong(values.get(2));
1005 entry.txBytes = Long.parseLong(values.get(9));
1006 entry.txPackets = Long.parseLong(values.get(10));
1007
1008 activeIfaces.add(entry.iface);
1009 stats.addValues(entry);
1010 } catch (NumberFormatException e) {
1011 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
1012 }
1013 }
1014 } catch (IOException e) {
1015 Slog.w(TAG, "problem parsing stats: " + e);
1016 } finally {
1017 IoUtils.closeQuietly(reader);
1018 }
1019
1020 if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces);
1021
1022 // splice in stats from any disabled ifaces
1023 if (mBandwidthControlEnabled) {
1024 final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface));
1025 xtIfaces.removeAll(activeIfaces);
1026
1027 for (String iface : xtIfaces) {
1028 final File ifacePath = new File(mStatsXtIface, iface);
1029
1030 entry.iface = iface;
1031 entry.uid = UID_ALL;
1032 entry.tag = TAG_NONE;
1033 entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes"));
1034 entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets"));
1035 entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes"));
1036 entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets"));
1037
1038 stats.addValues(entry);
1039 }
1040
1041 if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces);
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001042 }
1043
Jeff Sharkey4a971222011-06-11 22:16:55 -07001044 return stats;
San Mehat91cac642010-03-31 14:31:36 -07001045 }
1046
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001047 @Override
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001048 public NetworkStats getNetworkStatsDetail() {
1049 mContext.enforceCallingOrSelfPermission(
1050 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1051
Jeff Sharkey350083e2011-06-29 10:45:16 -07001052 if (mBandwidthControlEnabled) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001053 return getNetworkStatsDetailNetfilter(UID_ALL);
1054 } else {
1055 return getNetworkStatsDetailUidstat(UID_ALL);
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001056 }
San Mehat91cac642010-03-31 14:31:36 -07001057 }
1058
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001059 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001060 public void setInterfaceQuota(String iface, long quota) {
1061 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1062
Jeff Sharkey350083e2011-06-29 10:45:16 -07001063 // silently discard when control disabled
1064 // TODO: eventually migrate to be always enabled
1065 if (!mBandwidthControlEnabled) return;
1066
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001067 synchronized (mInterfaceQuota) {
1068 if (mInterfaceQuota.contains(iface)) {
1069 // TODO: eventually consider throwing
1070 return;
1071 }
1072
1073 final StringBuilder command = new StringBuilder();
1074 command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
1075
1076 try {
1077 // TODO: add support for quota shared across interfaces
1078 mConnector.doCommand(command.toString());
1079 mInterfaceQuota.add(iface);
1080 } catch (NativeDaemonConnectorException e) {
1081 throw new IllegalStateException("Error communicating to native daemon", e);
1082 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -07001083 }
1084 }
1085
1086 @Override
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001087 public void removeInterfaceQuota(String iface) {
1088 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1089
Jeff Sharkey350083e2011-06-29 10:45:16 -07001090 // silently discard when control disabled
1091 // TODO: eventually migrate to be always enabled
1092 if (!mBandwidthControlEnabled) return;
1093
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001094 synchronized (mInterfaceQuota) {
1095 if (!mInterfaceQuota.contains(iface)) {
1096 // TODO: eventually consider throwing
1097 return;
1098 }
1099
1100 final StringBuilder command = new StringBuilder();
1101 command.append("bandwidth removeiquota ").append(iface);
1102
1103 try {
1104 // TODO: add support for quota shared across interfaces
1105 mConnector.doCommand(command.toString());
1106 mInterfaceQuota.remove(iface);
1107 } catch (NativeDaemonConnectorException e) {
1108 throw new IllegalStateException("Error communicating to native daemon", e);
1109 }
1110 }
1111 }
1112
1113 @Override
1114 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1115 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
1116
Jeff Sharkey350083e2011-06-29 10:45:16 -07001117 // silently discard when control disabled
1118 // TODO: eventually migrate to be always enabled
1119 if (!mBandwidthControlEnabled) return;
1120
Jeff Sharkeyb3f19ca2011-06-29 23:54:13 -07001121 synchronized (mUidRejectOnQuota) {
1122 final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1123 if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1124 // TODO: eventually consider throwing
1125 return;
1126 }
1127
1128 final StringBuilder command = new StringBuilder();
1129 command.append("bandwidth");
1130 if (rejectOnQuotaInterfaces) {
1131 command.append(" addnaughtyapps");
1132 } else {
1133 command.append(" removenaughtyapps");
1134 }
1135 command.append(" ").append(uid);
1136
1137 try {
1138 mConnector.doCommand(command.toString());
1139 if (rejectOnQuotaInterfaces) {
1140 mUidRejectOnQuota.put(uid, true);
1141 } else {
1142 mUidRejectOnQuota.delete(uid);
1143 }
1144 } catch (NativeDaemonConnectorException e) {
1145 throw new IllegalStateException("Error communicating to native daemon", e);
1146 }
Ashish Sharma50fd36d2011-06-15 19:34:53 -07001147 }
1148 }
1149
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001150 public NetworkStats getNetworkStatsUidDetail(int uid) {
1151 if (Binder.getCallingUid() != uid) {
1152 mContext.enforceCallingOrSelfPermission(
1153 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1154 }
1155
Jeff Sharkey350083e2011-06-29 10:45:16 -07001156 if (mBandwidthControlEnabled) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001157 return getNetworkStatsDetailNetfilter(uid);
1158 } else {
1159 return getNetworkStatsDetailUidstat(uid);
1160 }
1161 }
1162
1163 /**
1164 * Build {@link NetworkStats} with detailed UID statistics.
1165 */
1166 private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
1167 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001168 final NetworkStats.Entry entry = new NetworkStats.Entry();
1169
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001170 final ArrayList<String> keys = Lists.newArrayList();
1171 final ArrayList<String> values = Lists.newArrayList();
1172 final HashMap<String, String> parsed = Maps.newHashMap();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001173
1174 BufferedReader reader = null;
1175 try {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001176 reader = new BufferedReader(new FileReader(mStatsXtUid));
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001177
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001178 // parse first line as header
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001179 String line = reader.readLine();
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001180 splitLine(line, keys);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001181
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001182 // parse remaining lines
1183 while ((line = reader.readLine()) != null) {
1184 splitLine(line, values);
1185 parseLine(keys, values, parsed);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001186
1187 try {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001188 // TODO: add rxPackets/txPackets once kernel exports
1189 entry.iface = parsed.get(KEY_IFACE);
1190 entry.tag = NetworkManagementSocketTagger.kernelToTag(
Jesse Wilson8568db52011-06-28 19:06:31 -07001191 parsed.get(KEY_TAG_HEX));
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001192 entry.uid = Integer.parseInt(parsed.get(KEY_UID));
1193 entry.rxBytes = Long.parseLong(parsed.get(KEY_RX));
1194 entry.txBytes = Long.parseLong(parsed.get(KEY_TX));
Jeff Sharkeyd03fd3f2011-06-19 20:55:09 -07001195
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001196 if (limitUid == UID_ALL || limitUid == entry.uid) {
1197 stats.addValues(entry);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001198 }
1199 } catch (NumberFormatException e) {
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001200 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001201 }
1202 }
1203 } catch (IOException e) {
1204 Slog.w(TAG, "problem parsing stats: " + e);
1205 } finally {
1206 IoUtils.closeQuietly(reader);
1207 }
1208
Jeff Sharkey4a971222011-06-11 22:16:55 -07001209 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001210 }
1211
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001212 /**
1213 * Build {@link NetworkStats} with detailed UID statistics.
1214 *
1215 * @deprecated since this uses older "uid_stat" data, and doesn't provide
1216 * tag-level granularity or additional variables.
1217 */
1218 @Deprecated
1219 private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
1220 final String[] knownUids;
1221 if (limitUid == UID_ALL) {
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001222 knownUids = fileListWithoutNull(mStatsUid);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001223 } else {
1224 knownUids = new String[] { String.valueOf(limitUid) };
1225 }
1226
1227 final NetworkStats stats = new NetworkStats(
1228 SystemClock.elapsedRealtime(), knownUids.length);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001229 final NetworkStats.Entry entry = new NetworkStats.Entry();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001230 for (String uid : knownUids) {
1231 final int uidInt = Integer.parseInt(uid);
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001232 final File uidPath = new File(mStatsUid, uid);
1233
1234 entry.iface = IFACE_ALL;
1235 entry.uid = uidInt;
1236 entry.tag = TAG_NONE;
1237 entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
1238 entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt"));
1239 entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
1240 entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt"));
1241
1242 stats.addValues(entry);
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -07001243 }
1244
1245 return stats;
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001246 }
1247
San Mehatf0db6e12010-04-07 15:22:10 -07001248 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
San Mehat91cac642010-03-31 14:31:36 -07001249 mContext.enforceCallingOrSelfPermission(
1250 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Kenny Roota80ce062010-06-01 13:23:53 -07001251 try {
1252 mConnector.doCommand(String.format(
1253 "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
1254 } catch (NativeDaemonConnectorException e) {
1255 Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
1256 }
San Mehat91cac642010-03-31 14:31:36 -07001257 }
1258
1259 private int getInterfaceThrottle(String iface, boolean rx) {
1260 mContext.enforceCallingOrSelfPermission(
1261 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
1262 try {
Kenny Roota80ce062010-06-01 13:23:53 -07001263 String rsp;
1264 try {
1265 rsp = mConnector.doCommand(
1266 String.format("interface getthrottle %s %s", iface,
1267 (rx ? "rx" : "tx"))).get(0);
1268 } catch (NativeDaemonConnectorException e) {
1269 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
1270 return -1;
1271 }
1272
1273 String[] tok = rsp.split(" ");
1274 if (tok.length < 2) {
1275 Slog.e(TAG, "Malformed response to getthrottle command");
1276 return -1;
1277 }
1278
San Mehat91cac642010-03-31 14:31:36 -07001279 int code;
1280 try {
1281 code = Integer.parseInt(tok[0]);
1282 } catch (NumberFormatException nfe) {
1283 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
1284 return -1;
1285 }
1286 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
1287 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
1288 Slog.e(TAG, String.format("Unexpected response code %d", code));
1289 return -1;
1290 }
1291 return Integer.parseInt(tok[1]);
1292 } catch (Exception e) {
1293 Slog.e(TAG, String.format(
1294 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
1295 }
1296 return -1;
1297 }
1298
1299 public int getInterfaceRxThrottle(String iface) {
1300 return getInterfaceThrottle(iface, true);
1301 }
1302
1303 public int getInterfaceTxThrottle(String iface) {
1304 return getInterfaceThrottle(iface, false);
1305 }
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001306
1307 /**
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001308 * Split given line into {@link ArrayList}.
1309 */
1310 private static void splitLine(String line, ArrayList<String> outSplit) {
1311 outSplit.clear();
1312
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001313 final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:");
Jeff Sharkey4414cea2011-06-24 17:05:24 -07001314 while (t.hasMoreTokens()) {
1315 outSplit.add(t.nextToken());
1316 }
1317 }
1318
1319 /**
1320 * Zip the two given {@link ArrayList} as key and value pairs into
1321 * {@link HashMap}.
1322 */
1323 private static void parseLine(
1324 ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
1325 outParsed.clear();
1326
1327 final int size = Math.min(keys.size(), values.size());
1328 for (int i = 0; i < size; i++) {
1329 outParsed.put(keys.get(i), values.get(i));
1330 }
1331 }
1332
1333 /**
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001334 * Utility method to read a single plain-text {@link Long} from the given
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001335 * {@link File}, usually from a {@code /proc/} filesystem.
1336 */
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001337 private static long readSingleLongFromFile(File file) {
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001338 try {
Jeff Sharkeyeedcb952011-05-17 14:55:15 -07001339 final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
1340 return Long.parseLong(new String(buffer).trim());
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001341 } catch (NumberFormatException e) {
1342 return -1;
1343 } catch (IOException e) {
1344 return -1;
Jeff Sharkey9a13f362011-04-26 16:25:36 -07001345 }
1346 }
Jean-Baptiste Querud5299ff2011-07-07 08:46:09 -07001347
Jeff Sharkeyfd8be3e2011-07-11 14:36:15 -07001348 /**
1349 * Wrapper for {@link File#list()} that returns empty array instead of
1350 * {@code null}.
1351 */
1352 private static String[] fileListWithoutNull(File file) {
1353 final String[] list = file.list();
1354 return list != null ? list : new String[0];
1355 }
1356
Mattias Falk7475c0c2011-04-04 16:10:36 +02001357 public void setDefaultInterfaceForDns(String iface) throws IllegalStateException {
1358 mContext.enforceCallingOrSelfPermission(
1359 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
1360 try {
1361 String cmd = "resolver setdefaultif " + iface;
1362
1363 mConnector.doCommand(cmd);
1364 } catch (NativeDaemonConnectorException e) {
1365 throw new IllegalStateException(
1366 "Error communicating with native daemon to set default interface", e);
1367 }
1368 }
1369
1370 public void setDnsServersForInterface(String iface, String[] servers)
1371 throws IllegalStateException {
1372 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
1373 "NetworkManagementService");
1374 try {
1375 String cmd = "resolver setifdns " + iface;
1376 for (String s : servers) {
1377 if (s != null && !"0.0.0.0".equals(s) &&
1378 !"::".equals(s) && !"0:0:0:0:0:0:0:0".equals(s)) {
1379 cmd += " " + InetAddress.getByName(s).getHostAddress();
1380 }
1381 }
1382
1383 mConnector.doCommand(cmd);
1384 } catch (UnknownHostException e) {
1385 throw new IllegalStateException("failed to resolve dns address.", e);
1386 } catch (NativeDaemonConnectorException e) {
1387 throw new IllegalStateException(
1388 "Error communicating with native deamon to set dns for interface", e);
1389 }
1390 }
1391
1392 public void flushDefaultDnsCache() throws IllegalStateException {
1393 mContext.enforceCallingOrSelfPermission(
1394 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
1395 try {
1396 String cmd = "resolver flushdefaultif";
1397
1398 mConnector.doCommand(cmd);
1399 } catch (NativeDaemonConnectorException e) {
1400 throw new IllegalStateException(
1401 "Error communicating with native deamon to flush default interface", e);
1402 }
1403 }
1404
1405 public void flushInterfaceDnsCache(String iface) throws IllegalStateException {
1406 mContext.enforceCallingOrSelfPermission(
1407 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
1408 try {
1409 String cmd = "resolver flushif " + iface;
1410
1411 mConnector.doCommand(cmd);
1412 } catch (NativeDaemonConnectorException e) {
1413 throw new IllegalStateException(
1414 "Error communicating with native deamon to flush interface " + iface, e);
1415 }
1416 }
San Mehat873f2142010-01-14 10:25:07 -08001417}