blob: b9bb16f15e6a53cc3cd61c6234107e1d70c5e0b2 [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
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.res.Resources;
25import android.content.pm.PackageManager;
26import android.net.Uri;
San Mehated4fc8a2010-01-22 12:28:36 -080027import android.net.InterfaceConfiguration;
San Mehat4d02d002010-01-22 16:07:46 -080028import android.net.INetworkManagementEventObserver;
San Mehat873f2142010-01-14 10:25:07 -080029import android.os.INetworkManagementService;
30import android.os.Handler;
31import android.text.TextUtils;
32import android.util.Log;
33import java.util.ArrayList;
San Mehated4fc8a2010-01-22 12:28:36 -080034import java.util.StringTokenizer;
San Mehat873f2142010-01-14 10:25:07 -080035import android.provider.Settings;
36import android.content.ContentResolver;
37import android.database.ContentObserver;
38
39import java.io.File;
40import java.io.FileReader;
41import java.lang.IllegalStateException;
42
43import java.net.InetAddress;
44import java.net.UnknownHostException;
45
46/**
47 * @hide
48 */
49class NetworkManagementService extends INetworkManagementService.Stub {
50
51 private static final String TAG = "NetworkManagmentService";
52
53 class NetdResponseCode {
54 public static final int InterfaceListResult = 110;
55 public static final int TetherInterfaceListResult = 111;
56 public static final int TetherDnsFwdTgtListResult = 112;
San Mehat72759df2010-01-19 13:50:37 -080057 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080058
59 public static final int TetherStatusResult = 210;
60 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080061 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080062 public static final int SoftapStatusResult = 214;
63 public static final int UsbRNDISStatusResult = 215;
64
65 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -080066 }
67
68 /**
69 * Binder context for this service
70 */
71 private Context mContext;
72
73 /**
74 * connector object for communicating with netd
75 */
76 private NativeDaemonConnector mConnector;
77
San Mehat4d02d002010-01-22 16:07:46 -080078 private ArrayList<INetworkManagementEventObserver> mObservers;
79
San Mehat873f2142010-01-14 10:25:07 -080080 /**
81 * Constructs a new NetworkManagementService instance
82 *
83 * @param context Binder context for this service
84 */
San Mehatd1df8ac2010-01-26 06:17:26 -080085 public NetworkManagementService(Context context) {
San Mehat873f2142010-01-14 10:25:07 -080086 mContext = context;
87
San Mehat4d02d002010-01-22 16:07:46 -080088 mObservers = new ArrayList<INetworkManagementEventObserver>();
89
San Mehat873f2142010-01-14 10:25:07 -080090 mConnector = new NativeDaemonConnector(
91 new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
92 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
93 thread.start();
94 }
95
San Mehat4d02d002010-01-22 16:07:46 -080096 public void registerObserver(INetworkManagementEventObserver obs) {
97 Log.d(TAG, "Registering observer");
98 mObservers.add(obs);
99 }
100
101 public void unregisterObserver(INetworkManagementEventObserver obs) {
102 Log.d(TAG, "Unregistering observer");
103 mObservers.remove(mObservers.indexOf(obs));
104 }
105
106 /**
107 * Notify our observers of an interface link status change
108 */
109 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
110 for (INetworkManagementEventObserver obs : mObservers) {
111 try {
112 obs.interfaceLinkStatusChanged(iface, link);
113 } catch (Exception ex) {
114 Log.w(TAG, "Observer notifier failed", ex);
115 }
116 }
117 }
118
119 /**
120 * Notify our observers of an interface addition.
121 */
122 private void notifyInterfaceAdded(String iface) {
123 for (INetworkManagementEventObserver obs : mObservers) {
124 try {
125 obs.interfaceAdded(iface);
126 } catch (Exception ex) {
127 Log.w(TAG, "Observer notifier failed", ex);
128 }
129 }
130 }
131
132 /**
133 * Notify our observers of an interface removal.
134 */
135 private void notifyInterfaceRemoved(String iface) {
136 for (INetworkManagementEventObserver obs : mObservers) {
137 try {
138 obs.interfaceRemoved(iface);
139 } catch (Exception ex) {
140 Log.w(TAG, "Observer notifier failed", ex);
141 }
142 }
143 }
144
145
San Mehat873f2142010-01-14 10:25:07 -0800146 //
147 // Netd Callback handling
148 //
149
150 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
151 public void onDaemonConnected() {
152 new Thread() {
153 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800154 }
155 }.start();
156 }
157 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800158 if (code == NetdResponseCode.InterfaceChange) {
159 /*
160 * a network interface change occured
161 * Format: "NNN Iface added <name>"
162 * "NNN Iface removed <name>"
163 * "NNN Iface changed <name> <up/down>"
164 */
165 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
166 throw new IllegalStateException(
167 String.format("Invalid event from daemon (%s)", raw));
168 }
169 if (cooked[2].equals("added")) {
170 notifyInterfaceAdded(cooked[3]);
171 return true;
172 } else if (cooked[2].equals("removed")) {
173 notifyInterfaceRemoved(cooked[3]);
174 return true;
175 } else if (cooked[2].equals("changed") && cooked.length == 5) {
176 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
177 return true;
178 }
179 throw new IllegalStateException(
180 String.format("Invalid event from daemon (%s)", raw));
181 }
182 return false;
San Mehat873f2142010-01-14 10:25:07 -0800183 }
184 }
185
San Mehated4fc8a2010-01-22 12:28:36 -0800186 private static int stringToIpAddr(String addrString) throws UnknownHostException {
187 try {
188 String[] parts = addrString.split("\\.");
189 if (parts.length != 4) {
190 throw new UnknownHostException(addrString);
191 }
192
193 int a = Integer.parseInt(parts[0]) ;
194 int b = Integer.parseInt(parts[1]) << 8;
195 int c = Integer.parseInt(parts[2]) << 16;
196 int d = Integer.parseInt(parts[3]) << 24;
197
198 return a | b | c | d;
199 } catch (NumberFormatException ex) {
200 throw new UnknownHostException(addrString);
201 }
202 }
203
204 public static String intToIpString(int i) {
205 return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." +
206 (i & 0xFF);
207 }
208
San Mehat873f2142010-01-14 10:25:07 -0800209 //
210 // INetworkManagementService members
211 //
212
213 public String[] listInterfaces() throws IllegalStateException {
214 mContext.enforceCallingOrSelfPermission(
215 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
216
San Mehated4fc8a2010-01-22 12:28:36 -0800217 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
218 }
219
220 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
221 String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
222 Log.d(TAG, String.format("rsp <%s>", rsp));
223
224 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
225 StringTokenizer st = new StringTokenizer(rsp);
226
227 try {
228 int code = Integer.parseInt(st.nextToken(" "));
229 if (code != NetdResponseCode.InterfaceGetCfgResult) {
230 throw new IllegalStateException(
231 String.format("Expected code %d, but got %d",
232 NetdResponseCode.InterfaceGetCfgResult, code));
233 }
234 } catch (NumberFormatException nfe) {
235 throw new IllegalStateException(
236 String.format("Invalid response from daemon (%s)", rsp));
237 }
238
239 InterfaceConfiguration cfg = new InterfaceConfiguration();
240 cfg.hwAddr = st.nextToken(" ");
241 try {
242 cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
243 } catch (UnknownHostException uhe) {
244 Log.e(TAG, "Failed to parse ipaddr", uhe);
245 cfg.ipAddr = 0;
246 }
247
248 try {
249 cfg.netmask = stringToIpAddr(st.nextToken(" "));
250 } catch (UnknownHostException uhe) {
251 Log.e(TAG, "Failed to parse netmask", uhe);
252 cfg.netmask = 0;
253 }
254 cfg.interfaceFlags = st.nextToken("]");
255 Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
256 return cfg;
257 }
258
259 public void setInterfaceConfig(
260 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
261 String cmd = String.format("interface setcfg %s %s %s", iface,
262 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
263 mConnector.doCommand(cmd);
San Mehat873f2142010-01-14 10:25:07 -0800264 }
265
266 public void shutdown() {
267 if (mContext.checkCallingOrSelfPermission(
268 android.Manifest.permission.SHUTDOWN)
269 != PackageManager.PERMISSION_GRANTED) {
270 throw new SecurityException("Requires SHUTDOWN permission");
271 }
272
273 Log.d(TAG, "Shutting down");
274 }
275
276 public boolean getIpForwardingEnabled() throws IllegalStateException{
277 mContext.enforceCallingOrSelfPermission(
278 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
279
280 ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
281
282 for (String line : rsp) {
283 String []tok = line.split(" ");
284 int code = Integer.parseInt(tok[0]);
285 if (code == NetdResponseCode.IpFwdStatusResult) {
286 // 211 Forwarding <enabled/disabled>
287 if (tok.length !=2) {
288 throw new IllegalStateException(
289 String.format("Malformatted list entry '%s'", line));
290 }
291 if (tok[2].equals("enabled"))
292 return true;
293 return false;
294 } else {
295 throw new IllegalStateException(String.format("Unexpected response code %d", code));
296 }
297 }
298 throw new IllegalStateException("Got an empty response");
299 }
300
301 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
302 mContext.enforceCallingOrSelfPermission(
303 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
304 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
305 }
306
307 public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
308 throws IllegalStateException {
309 mContext.enforceCallingOrSelfPermission(
310 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
311 mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
312 }
313
314 public void stopTethering() throws IllegalStateException {
315 mContext.enforceCallingOrSelfPermission(
316 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
317 mConnector.doCommand("tether stop");
318 }
319
320 public boolean isTetheringStarted() throws IllegalStateException {
321 mContext.enforceCallingOrSelfPermission(
322 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
323
324 ArrayList<String> rsp = mConnector.doCommand("tether status");
325
326 for (String line : rsp) {
327 String []tok = line.split(" ");
328 int code = Integer.parseInt(tok[0]);
329 if (code == NetdResponseCode.TetherStatusResult) {
330 // XXX: Tethering services <started/stopped> <TBD>...
331 if (tok[2].equals("started"))
332 return true;
333 return false;
334 } else {
335 throw new IllegalStateException(String.format("Unexpected response code %d", code));
336 }
337 }
338 throw new IllegalStateException("Got an empty response");
339 }
340
341 public void tetherInterface(String iface) throws IllegalStateException {
342 mContext.enforceCallingOrSelfPermission(
343 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
344 mConnector.doCommand("tether interface add " + iface);
345 }
346
347 public void untetherInterface(String iface) {
348 mContext.enforceCallingOrSelfPermission(
349 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
350 mConnector.doCommand("tether interface remove " + iface);
351 }
352
353 public String[] listTetheredInterfaces() throws IllegalStateException {
354 mContext.enforceCallingOrSelfPermission(
355 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
San Mehat72759df2010-01-19 13:50:37 -0800356 return mConnector.doListCommand(
357 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
San Mehat873f2142010-01-14 10:25:07 -0800358 }
359
360 public void setDnsForwarders(String[] dns) throws IllegalStateException {
361 mContext.enforceCallingOrSelfPermission(
362 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
363 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800364 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800365 for (String s : dns) {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800366 cmd += " " + InetAddress.getByName(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800367 }
368 mConnector.doCommand(cmd);
369 } catch (UnknownHostException e) {
370 throw new IllegalStateException("Error resolving dns name", e);
371 }
372 }
373
374 public String[] getDnsForwarders() throws IllegalStateException {
375 mContext.enforceCallingOrSelfPermission(
376 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
San Mehat72759df2010-01-19 13:50:37 -0800377 return mConnector.doListCommand(
378 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
San Mehat873f2142010-01-14 10:25:07 -0800379 }
380
381 public void enableNat(String internalInterface, String externalInterface)
382 throws IllegalStateException {
383 mContext.enforceCallingOrSelfPermission(
384 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
385 mConnector.doCommand(
386 String.format("nat enable %s %s", internalInterface, externalInterface));
387 }
388
389 public void disableNat(String internalInterface, String externalInterface)
390 throws IllegalStateException {
391 mContext.enforceCallingOrSelfPermission(
392 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
393 mConnector.doCommand(
394 String.format("nat disable %s %s", internalInterface, externalInterface));
395 }
San Mehat72759df2010-01-19 13:50:37 -0800396
397 public String[] listTtys() throws IllegalStateException {
398 mContext.enforceCallingOrSelfPermission(
399 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
400 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
401 }
402
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800403 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
404 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800405 try {
406 mContext.enforceCallingOrSelfPermission(
407 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800408 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
409 InetAddress.getByName(localAddr).getHostAddress(),
410 InetAddress.getByName(remoteAddr).getHostAddress(),
411 InetAddress.getByName(dns1Addr).getHostAddress(),
412 InetAddress.getByName(dns2Addr).getHostAddress()));
San Mehat72759df2010-01-19 13:50:37 -0800413 } catch (UnknownHostException e) {
414 throw new IllegalStateException("Error resolving addr", e);
415 }
416 }
417
418 public void detachPppd(String tty) throws IllegalStateException {
419 mContext.enforceCallingOrSelfPermission(
420 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
421 mConnector.doCommand(String.format("pppd detach %s", tty));
422 }
San Mehat873f2142010-01-14 10:25:07 -0800423}