Loading core/java/android/net/ConnectivityManager.java +41 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.net.InetAddress; Loading Loading @@ -756,4 +758,43 @@ public class ConnectivityManager { } catch (RemoteException e) { } } /** * Protect a socket from routing changes. This method is limited to VPN * applications, and it is always hidden to avoid direct use. * @hide */ public void protectVpn(ParcelFileDescriptor socket) { try { mService.protectVpn(socket); } catch (RemoteException e) { } } /** * Prepare for a VPN application. This method is limited to VpnDialogs, * and it is always hidden to avoid direct use. * @hide */ public String prepareVpn(String packageName) { try { return mService.prepareVpn(packageName); } catch (RemoteException e) { return null; } } /** * Configure a TUN interface and return its file descriptor. Parameters * are encoded and opaque to this class. This method is limited to VPN * applications, and it is always hidden to avoid direct use. * @hide */ public ParcelFileDescriptor establishVpn(Bundle config) { try { return mService.establishVpn(config); } catch (RemoteException e) { return null; } } } core/java/android/net/IConnectivityManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -20,7 +20,9 @@ import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkState; import android.net.ProxyProperties; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; /** * Interface that answers queries about, and allows changing, the Loading Loading @@ -95,4 +97,10 @@ interface IConnectivityManager ProxyProperties getProxy(); void setDataDependency(int networkType, boolean met); void protectVpn(in ParcelFileDescriptor socket); String prepareVpn(String packageName); ParcelFileDescriptor establishVpn(in Bundle config); } core/res/res/values/strings.xml +8 −1 Original line number Diff line number Diff line Loading @@ -1330,7 +1330,7 @@ <string name="permlab_vpn">intercept and modify all network traffic</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_vpn">Allows an application to intercept and inspect all network traffic, for example to establish a VPN connection. inspect all network traffic to establish a VPN connection. Malicious applications may monitor, redirect, or modify network packets without your knowledge.</string> Loading Loading @@ -2725,6 +2725,13 @@ <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string> <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string> <!-- Ticker text to show when VPN is active. --> <string name="vpn_ticker">Activating <xliff:g id="app">%s</xliff:g> VPN...</string> <!-- The title of the notification when VPN is active. --> <string name="vpn_title"><xliff:g id="app">%s</xliff:g> VPN is active</string> <!-- The text of the notification when VPN is active. --> <string name="vpn_text">VPN is connected to <xliff:g id="profile">%s</xliff:g>. Tap to manage the network.</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> <string name="upload_file">Choose file</string> Loading services/java/com/android/server/ConnectivityService.java +68 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.net.RouteInfo; import android.net.vpn.VpnManager; import android.net.wifi.WifiStateTracker; import android.os.Binder; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; Loading @@ -55,6 +56,7 @@ import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; Loading @@ -67,6 +69,8 @@ import android.util.SparseIntArray; import com.android.internal.telephony.Phone; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.google.android.collect.Lists; import java.io.FileDescriptor; Loading Loading @@ -103,6 +107,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private Tethering mTethering; private boolean mTetheringConfigValid = false; private Vpn mVpn; /** Currently active network rules by UID. */ private SparseIntArray mUidRules = new SparseIntArray(); Loading Loading @@ -461,8 +467,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceRegexs().length != 0); mVpn = new Vpn(mContext, new VpnCallback()); try { nmService.registerObserver(mTethering); nmService.registerObserver(mVpn); } catch (RemoteException e) { loge("Error registering observer :" + e); } Loading Loading @@ -2358,6 +2367,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void loge(String s) { Slog.e(TAG, s); } int convertFeatureToNetworkType(String feature){ int networkType = -1; if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { Loading Loading @@ -2385,4 +2395,62 @@ public class ConnectivityService extends IConnectivityManager.Stub { } return value; } // @see ConnectivityManager#protectVpn(ParcelFileDescriptor) // Permission checks are done in Vpn class. @Override public void protectVpn(ParcelFileDescriptor socket) { mVpn.protect(socket, getDefaultInterface()); } // @see ConnectivityManager#prepareVpn(String) // Permission checks are done in Vpn class. @Override public String prepareVpn(String packageName) { return mVpn.prepare(packageName); } // @see ConnectivityManager#establishVpn(Bundle) // Permission checks are done in Vpn class. @Override public ParcelFileDescriptor establishVpn(Bundle config) { return mVpn.establish(config); } private String getDefaultInterface() { if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) { NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; if (tracker != null) { LinkProperties properties = tracker.getLinkProperties(); if (properties != null) { return properties.getInterfaceName(); } } } throw new IllegalStateException("No default interface"); } /** * Callback for VPN subsystem. Currently VPN is not adapted to the service * through NetworkStateTracker since it works differently. For example, it * needs to override DNS servers but never takes the default routes. It * relies on another data network, and it could keep existing connections * alive after reconnecting, switching between networks, or even resuming * from deep sleep. Calls from applications should be done synchronously * to avoid race conditions. As these are all hidden APIs, refactoring can * be done whenever a better abstraction is developed. */ public class VpnCallback { private VpnCallback() { } public synchronized void override(String[] dnsServers) { // TODO: override DNS servers and http proxy. } public synchronized void restore() { // TODO: restore VPN changes. } } } services/java/com/android/server/connectivity/Vpn.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.connectivity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.INetworkManagementEventObserver; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; import com.android.internal.R; import com.android.server.ConnectivityService.VpnCallback; /** * @hide */ public class Vpn extends INetworkManagementEventObserver.Stub { private final static String TAG = "Vpn"; private final static String VPN = android.Manifest.permission.VPN; private final Context mContext; private final VpnCallback mCallback; private String mPackageName; private String mInterfaceName; private String mDnsPropertyPrefix; public Vpn(Context context, VpnCallback callback) { mContext = context; mCallback = callback; } /** * Prepare for a VPN application. * * @param packageName The package name of the new VPN application. * @return The name of the current prepared package. */ public synchronized String prepare(String packageName) { // TODO: Check if the caller is VpnDialogs. if (packageName == null) { return mPackageName; } // Check the permission of the given application. PackageManager pm = mContext.getPackageManager(); if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(packageName + " does not have " + VPN); } // Reset the interface and hide the notification. if (mInterfaceName != null) { nativeReset(mInterfaceName); mInterfaceName = null; hideNotification(); // TODO: Send out a broadcast. } mPackageName = packageName; Log.i(TAG, "Prepared for " + packageName); return mPackageName; } /** * Protect a socket from routing changes by binding it to the given * interface. The socket is NOT closed by this method. * * @param socket The socket to be bound. * @param name The name of the interface. */ public void protect(ParcelFileDescriptor socket, String name) { mContext.enforceCallingPermission(VPN, "protect"); nativeProtect(socket.getFd(), name); } /** * Configure a TUN interface and return its file descriptor. * * @param configuration The parameters to configure the interface. * @return The file descriptor of the interface. */ public synchronized ParcelFileDescriptor establish(Bundle config) { // Check the permission of the caller. mContext.enforceCallingPermission(VPN, "establish"); // Check if the caller is already prepared. PackageManager pm = mContext.getPackageManager(); ApplicationInfo app = null; try { app = pm.getApplicationInfo(mPackageName, 0); } catch (Exception e) { throw new SecurityException("Not prepared"); } if (Binder.getCallingUid() != app.uid) { throw new SecurityException("Not prepared"); } // Unpack the config. // TODO: move constants into VpnBuilder. String session = config.getString("session"); String addresses = config.getString("addresses"); String routes = config.getString("routes"); String dnsServers = config.getString("dnsServers"); // Create interface and configure addresses and routes. ParcelFileDescriptor descriptor = nativeConfigure(addresses, routes); // Replace the interface and abort if it fails. try { String interfaceName = nativeGetName(descriptor.getFd()); if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) { nativeReset(mInterfaceName); } mInterfaceName = interfaceName; } catch (RuntimeException e) { try { descriptor.close(); } catch (Exception ex) { // ignore } throw e; } dnsServers = (dnsServers == null) ? "" : dnsServers.trim(); mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" ")); showNotification(pm, app, session); return descriptor; } public synchronized boolean onInterfaceRemoved(String name) { if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { hideNotification(); mInterfaceName = null; return true; } return false; } // INetworkManagementEventObserver.Stub public void interfaceLinkStatusChanged(String name, boolean up) { } // INetworkManagementEventObserver.Stub public void interfaceAdded(String name) { } // INetworkManagementEventObserver.Stub public synchronized void interfaceRemoved(String name) { if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { hideNotification(); mInterfaceName = null; mCallback.restore(); } } private void showNotification(PackageManager pm, ApplicationInfo app, String session) { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { // Load the icon and convert it into a bitmap. Drawable icon = app.loadIcon(pm); Bitmap bitmap = null; if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { int width = mContext.getResources().getDimensionPixelSize( android.R.dimen.notification_large_icon_width); int height = mContext.getResources().getDimensionPixelSize( android.R.dimen.notification_large_icon_height); icon.setBounds(0, 0, width, height); bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); icon.draw(new Canvas(bitmap)); } // Load the label. String label = app.loadLabel(pm).toString(); // If session is null, use the application name instead. if (session == null) { session = label; } // Build the intent. // TODO: move these into VpnBuilder. Intent intent = new Intent(); intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ManageDialog"); intent.putExtra("packageName", mPackageName); intent.putExtra("interfaceName", mInterfaceName); intent.putExtra("session", session); intent.putExtra("startTime", android.os.SystemClock.elapsedRealtime()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); // Build the notification. long identity = Binder.clearCallingIdentity(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.vpn_connected) .setLargeIcon(bitmap) .setTicker(mContext.getString(R.string.vpn_ticker, label)) .setContentTitle(mContext.getString(R.string.vpn_title, label)) .setContentText(mContext.getString(R.string.vpn_text, session)) .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0)) .setDefaults(Notification.DEFAULT_ALL) .setOngoing(true) .getNotification(); nm.notify(R.drawable.vpn_connected, notification); Binder.restoreCallingIdentity(identity); } } private void hideNotification() { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { long identity = Binder.clearCallingIdentity(); nm.cancel(R.drawable.vpn_connected); Binder.restoreCallingIdentity(identity); } } private native ParcelFileDescriptor nativeConfigure(String addresses, String routes); private native String nativeGetName(int fd); private native void nativeReset(String name); private native int nativeCheck(String name); private native void nativeProtect(int fd, String name); } Loading
core/java/android/net/ConnectivityManager.java +41 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import java.net.InetAddress; Loading Loading @@ -756,4 +758,43 @@ public class ConnectivityManager { } catch (RemoteException e) { } } /** * Protect a socket from routing changes. This method is limited to VPN * applications, and it is always hidden to avoid direct use. * @hide */ public void protectVpn(ParcelFileDescriptor socket) { try { mService.protectVpn(socket); } catch (RemoteException e) { } } /** * Prepare for a VPN application. This method is limited to VpnDialogs, * and it is always hidden to avoid direct use. * @hide */ public String prepareVpn(String packageName) { try { return mService.prepareVpn(packageName); } catch (RemoteException e) { return null; } } /** * Configure a TUN interface and return its file descriptor. Parameters * are encoded and opaque to this class. This method is limited to VPN * applications, and it is always hidden to avoid direct use. * @hide */ public ParcelFileDescriptor establishVpn(Bundle config) { try { return mService.establishVpn(config); } catch (RemoteException e) { return null; } } }
core/java/android/net/IConnectivityManager.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -20,7 +20,9 @@ import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkState; import android.net.ProxyProperties; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; /** * Interface that answers queries about, and allows changing, the Loading Loading @@ -95,4 +97,10 @@ interface IConnectivityManager ProxyProperties getProxy(); void setDataDependency(int networkType, boolean met); void protectVpn(in ParcelFileDescriptor socket); String prepareVpn(String packageName); ParcelFileDescriptor establishVpn(in Bundle config); }
core/res/res/values/strings.xml +8 −1 Original line number Diff line number Diff line Loading @@ -1330,7 +1330,7 @@ <string name="permlab_vpn">intercept and modify all network traffic</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_vpn">Allows an application to intercept and inspect all network traffic, for example to establish a VPN connection. inspect all network traffic to establish a VPN connection. Malicious applications may monitor, redirect, or modify network packets without your knowledge.</string> Loading Loading @@ -2725,6 +2725,13 @@ <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string> <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string> <!-- Ticker text to show when VPN is active. --> <string name="vpn_ticker">Activating <xliff:g id="app">%s</xliff:g> VPN...</string> <!-- The title of the notification when VPN is active. --> <string name="vpn_title"><xliff:g id="app">%s</xliff:g> VPN is active</string> <!-- The text of the notification when VPN is active. --> <string name="vpn_text">VPN is connected to <xliff:g id="profile">%s</xliff:g>. Tap to manage the network.</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> <string name="upload_file">Choose file</string> Loading
services/java/com/android/server/ConnectivityService.java +68 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.net.RouteInfo; import android.net.vpn.VpnManager; import android.net.wifi.WifiStateTracker; import android.os.Binder; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; Loading @@ -55,6 +56,7 @@ import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; Loading @@ -67,6 +69,8 @@ import android.util.SparseIntArray; import com.android.internal.telephony.Phone; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.google.android.collect.Lists; import java.io.FileDescriptor; Loading Loading @@ -103,6 +107,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private Tethering mTethering; private boolean mTetheringConfigValid = false; private Vpn mVpn; /** Currently active network rules by UID. */ private SparseIntArray mUidRules = new SparseIntArray(); Loading Loading @@ -461,8 +467,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceRegexs().length != 0); mVpn = new Vpn(mContext, new VpnCallback()); try { nmService.registerObserver(mTethering); nmService.registerObserver(mVpn); } catch (RemoteException e) { loge("Error registering observer :" + e); } Loading Loading @@ -2358,6 +2367,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void loge(String s) { Slog.e(TAG, s); } int convertFeatureToNetworkType(String feature){ int networkType = -1; if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { Loading Loading @@ -2385,4 +2395,62 @@ public class ConnectivityService extends IConnectivityManager.Stub { } return value; } // @see ConnectivityManager#protectVpn(ParcelFileDescriptor) // Permission checks are done in Vpn class. @Override public void protectVpn(ParcelFileDescriptor socket) { mVpn.protect(socket, getDefaultInterface()); } // @see ConnectivityManager#prepareVpn(String) // Permission checks are done in Vpn class. @Override public String prepareVpn(String packageName) { return mVpn.prepare(packageName); } // @see ConnectivityManager#establishVpn(Bundle) // Permission checks are done in Vpn class. @Override public ParcelFileDescriptor establishVpn(Bundle config) { return mVpn.establish(config); } private String getDefaultInterface() { if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) { NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork]; if (tracker != null) { LinkProperties properties = tracker.getLinkProperties(); if (properties != null) { return properties.getInterfaceName(); } } } throw new IllegalStateException("No default interface"); } /** * Callback for VPN subsystem. Currently VPN is not adapted to the service * through NetworkStateTracker since it works differently. For example, it * needs to override DNS servers but never takes the default routes. It * relies on another data network, and it could keep existing connections * alive after reconnecting, switching between networks, or even resuming * from deep sleep. Calls from applications should be done synchronously * to avoid race conditions. As these are all hidden APIs, refactoring can * be done whenever a better abstraction is developed. */ public class VpnCallback { private VpnCallback() { } public synchronized void override(String[] dnsServers) { // TODO: override DNS servers and http proxy. } public synchronized void restore() { // TODO: restore VPN changes. } } }
services/java/com/android/server/connectivity/Vpn.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright (C) 2011 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.connectivity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.INetworkManagementEventObserver; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; import com.android.internal.R; import com.android.server.ConnectivityService.VpnCallback; /** * @hide */ public class Vpn extends INetworkManagementEventObserver.Stub { private final static String TAG = "Vpn"; private final static String VPN = android.Manifest.permission.VPN; private final Context mContext; private final VpnCallback mCallback; private String mPackageName; private String mInterfaceName; private String mDnsPropertyPrefix; public Vpn(Context context, VpnCallback callback) { mContext = context; mCallback = callback; } /** * Prepare for a VPN application. * * @param packageName The package name of the new VPN application. * @return The name of the current prepared package. */ public synchronized String prepare(String packageName) { // TODO: Check if the caller is VpnDialogs. if (packageName == null) { return mPackageName; } // Check the permission of the given application. PackageManager pm = mContext.getPackageManager(); if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(packageName + " does not have " + VPN); } // Reset the interface and hide the notification. if (mInterfaceName != null) { nativeReset(mInterfaceName); mInterfaceName = null; hideNotification(); // TODO: Send out a broadcast. } mPackageName = packageName; Log.i(TAG, "Prepared for " + packageName); return mPackageName; } /** * Protect a socket from routing changes by binding it to the given * interface. The socket is NOT closed by this method. * * @param socket The socket to be bound. * @param name The name of the interface. */ public void protect(ParcelFileDescriptor socket, String name) { mContext.enforceCallingPermission(VPN, "protect"); nativeProtect(socket.getFd(), name); } /** * Configure a TUN interface and return its file descriptor. * * @param configuration The parameters to configure the interface. * @return The file descriptor of the interface. */ public synchronized ParcelFileDescriptor establish(Bundle config) { // Check the permission of the caller. mContext.enforceCallingPermission(VPN, "establish"); // Check if the caller is already prepared. PackageManager pm = mContext.getPackageManager(); ApplicationInfo app = null; try { app = pm.getApplicationInfo(mPackageName, 0); } catch (Exception e) { throw new SecurityException("Not prepared"); } if (Binder.getCallingUid() != app.uid) { throw new SecurityException("Not prepared"); } // Unpack the config. // TODO: move constants into VpnBuilder. String session = config.getString("session"); String addresses = config.getString("addresses"); String routes = config.getString("routes"); String dnsServers = config.getString("dnsServers"); // Create interface and configure addresses and routes. ParcelFileDescriptor descriptor = nativeConfigure(addresses, routes); // Replace the interface and abort if it fails. try { String interfaceName = nativeGetName(descriptor.getFd()); if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) { nativeReset(mInterfaceName); } mInterfaceName = interfaceName; } catch (RuntimeException e) { try { descriptor.close(); } catch (Exception ex) { // ignore } throw e; } dnsServers = (dnsServers == null) ? "" : dnsServers.trim(); mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" ")); showNotification(pm, app, session); return descriptor; } public synchronized boolean onInterfaceRemoved(String name) { if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { hideNotification(); mInterfaceName = null; return true; } return false; } // INetworkManagementEventObserver.Stub public void interfaceLinkStatusChanged(String name, boolean up) { } // INetworkManagementEventObserver.Stub public void interfaceAdded(String name) { } // INetworkManagementEventObserver.Stub public synchronized void interfaceRemoved(String name) { if (name.equals(mInterfaceName) && nativeCheck(name) == 0) { hideNotification(); mInterfaceName = null; mCallback.restore(); } } private void showNotification(PackageManager pm, ApplicationInfo app, String session) { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { // Load the icon and convert it into a bitmap. Drawable icon = app.loadIcon(pm); Bitmap bitmap = null; if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) { int width = mContext.getResources().getDimensionPixelSize( android.R.dimen.notification_large_icon_width); int height = mContext.getResources().getDimensionPixelSize( android.R.dimen.notification_large_icon_height); icon.setBounds(0, 0, width, height); bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); icon.draw(new Canvas(bitmap)); } // Load the label. String label = app.loadLabel(pm).toString(); // If session is null, use the application name instead. if (session == null) { session = label; } // Build the intent. // TODO: move these into VpnBuilder. Intent intent = new Intent(); intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ManageDialog"); intent.putExtra("packageName", mPackageName); intent.putExtra("interfaceName", mInterfaceName); intent.putExtra("session", session); intent.putExtra("startTime", android.os.SystemClock.elapsedRealtime()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); // Build the notification. long identity = Binder.clearCallingIdentity(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.vpn_connected) .setLargeIcon(bitmap) .setTicker(mContext.getString(R.string.vpn_ticker, label)) .setContentTitle(mContext.getString(R.string.vpn_title, label)) .setContentText(mContext.getString(R.string.vpn_text, session)) .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0)) .setDefaults(Notification.DEFAULT_ALL) .setOngoing(true) .getNotification(); nm.notify(R.drawable.vpn_connected, notification); Binder.restoreCallingIdentity(identity); } } private void hideNotification() { NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { long identity = Binder.clearCallingIdentity(); nm.cancel(R.drawable.vpn_connected); Binder.restoreCallingIdentity(identity); } } private native ParcelFileDescriptor nativeConfigure(String addresses, String routes); private native String nativeGetName(int fd); private native void nativeReset(String name); private native int nativeCheck(String name); private native void nativeProtect(int fd, String name); }