diff --git a/Android.mk b/Android.mk index 7c7bb6275d4e1..d6beac5e64f6a 100644 --- a/Android.mk +++ b/Android.mk @@ -111,6 +111,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/ICheckinService.aidl \ core/java/android/os/IMessenger.aidl \ core/java/android/os/IMountService.aidl \ + core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetStatService.aidl \ core/java/android/os/IParentalControlCallback.aidl \ core/java/android/os/IPermissionController.aidl \ diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl new file mode 100644 index 0000000000000..bd6cabb4f060d --- /dev/null +++ b/core/java/android/os/INetworkManagementService.aidl @@ -0,0 +1,105 @@ +/* //device/java/android/android/os/INetworkManagementService.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.os; + +/** + * @hide + */ +interface INetworkManagementService +{ + /** + ** GENERAL + **/ + + /** + * Returns a list of currently known network interfaces + */ + String[] listInterfaces(); + + /** + * Shuts down the service + */ + void shutdown(); + + /** + ** TETHERING RELATED + **/ + + + /** + * Returns true if IP forwarding is enabled + */ + boolean getIpForwardingEnabled(); + + /** + * Enables/Disables IP Forwarding + */ + void setIpForwardingEnabled(boolean enabled); + + /** + * Start tethering services with the specified dhcp server range + */ + void startTethering(String dhcpRangeStart, String dhcpRangeEnd); + + /** + * Stop currently running tethering services + */ + void stopTethering(); + + /** + * Returns true if tethering services are started + */ + boolean isTetheringStarted(); + + /** + * Tethers the specified interface + */ + void tetherInterface(String iface); + + /** + * Untethers the specified interface + */ + void untetherInterface(String iface); + + /** + * Returns a list of currently tethered interfaces + */ + String[] listTetheredInterfaces(); + + /** + * Sets the list of DNS forwarders (in order of priority) + */ + void setDnsForwarders(in String[] dns); + + /** + * Returns the list of DNS fowarders (in order of priority) + */ + String[] getDnsForwarders(); + + /** + * Enables Network Address Translation between two interfaces. + * The address and netmask of the external interface is used for + * the NAT'ed network. + */ + void enableNat(String internalInterface, String externalInterface); + + /** + * Disables Network Address Translation between two interfaces. + */ + void disableNat(String internalInterface, String externalInterface); +} diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java new file mode 100644 index 0000000000000..97fa0cc229eff --- /dev/null +++ b/services/java/com/android/server/NetworkManagementService.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.INetworkManagementService; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Log; +import java.util.ArrayList; + +import android.provider.Settings; +import android.content.ContentResolver; +import android.database.ContentObserver; + +import java.io.File; +import java.io.FileReader; +import java.lang.IllegalStateException; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @hide + */ +class NetworkManagementService extends INetworkManagementService.Stub { + + private static final String TAG = "NetworkManagmentService"; + + class NetdResponseCode { + public static final int InterfaceListResult = 110; + public static final int TetherInterfaceListResult = 111; + public static final int TetherDnsFwdTgtListResult = 112; + + public static final int TetherStatusResult = 210; + public static final int IpFwdStatusResult = 211; + } + + /** + * Binder context for this service + */ + private Context mContext; + + /** + * connector object for communicating with netd + */ + private NativeDaemonConnector mConnector; + + /** + * Constructs a new NetworkManagementService instance + * + * @param context Binder context for this service + */ + private NetworkManagementService(Context context) { + mContext = context; + + mConnector = new NativeDaemonConnector( + new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); + Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); + thread.start(); + } + + // + // Netd Callback handling + // + + class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { + public void onDaemonConnected() { + new Thread() { + public void run() { + // XXX: Run some tests + } + }.start(); + } + public boolean onEvent(int code, String raw, String[] cooked) { + return false; + } + } + + // + // INetworkManagementService members + // + + public String[] listInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList rsp = mConnector.doCommand("list_interfaces"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.InterfaceListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void shutdown() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SHUTDOWN) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires SHUTDOWN permission"); + } + + Log.d(TAG, "Shutting down"); + } + + public boolean getIpForwardingEnabled() throws IllegalStateException{ + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList rsp = mConnector.doCommand("ipfwd status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.IpFwdStatusResult) { + // 211 Forwarding + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + if (tok[2].equals("enabled")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); + } + + public void startTethering(String dhcpRangeStart, String dhcpRangeEnd) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd)); + } + + public void stopTethering() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether stop"); + } + + public boolean isTetheringStarted() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList rsp = mConnector.doCommand("tether status"); + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherStatusResult) { + // XXX: Tethering services ... + if (tok[2].equals("started")) + return true; + return false; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void tetherInterface(String iface) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface add " + iface); + } + + public void untetherInterface(String iface) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand("tether interface remove " + iface); + } + + public String[] listTetheredInterfaces() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList rsp = mConnector.doCommand("tether interface list"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherInterfaceListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void setDnsForwarders(String[] dns) throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + try { + String cmd = "tether dns set "; + for (String s : dns) { + cmd += InetAddress.getByName(s).toString() + " "; + } + mConnector.doCommand(cmd); + } catch (UnknownHostException e) { + throw new IllegalStateException("Error resolving dns name", e); + } + } + + public String[] getDnsForwarders() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + + ArrayList rsp = mConnector.doCommand("tether dns list"); + + String[] rdata = new String[rsp.size()]; + int idx = 0; + + for (String line : rsp) { + String []tok = line.split(" "); + int code = Integer.parseInt(tok[0]); + if (code == NetdResponseCode.TetherDnsFwdTgtListResult) { + if (tok.length !=2) { + throw new IllegalStateException( + String.format("Malformatted list entry '%s'", line)); + } + rdata[idx++] = tok[1]; + } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { + return rdata; + } else { + throw new IllegalStateException(String.format("Unexpected response code %d", code)); + } + } + throw new IllegalStateException("Got an empty response"); + } + + public void enableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat enable %s %s", internalInterface, externalInterface)); + } + + public void disableNat(String internalInterface, String externalInterface) + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mConnector.doCommand( + String.format("nat disable %s %s", internalInterface, externalInterface)); + } +} +