Merge "Begin moving VPN to NetworkStateTracker pattern." into jb-mr1-dev
This commit is contained in:
153
core/java/android/net/BaseNetworkStateTracker.java
Normal file
153
core/java/android/net/BaseNetworkStateTracker.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Interface to control and observe state of a specific network, hiding
|
||||
* network-specific details from {@link ConnectivityManager}. Surfaces events
|
||||
* through the registered {@link Handler} to enable {@link ConnectivityManager}
|
||||
* to respond to state changes over time.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
|
||||
// TODO: better document threading expectations
|
||||
// TODO: migrate to make NetworkStateTracker abstract class
|
||||
|
||||
public static final String PROP_TCP_BUFFER_UNKNOWN = "net.tcp.buffersize.unknown";
|
||||
public static final String PROP_TCP_BUFFER_WIFI = "net.tcp.buffersize.wifi";
|
||||
|
||||
protected Context mContext;
|
||||
private Handler mTarget;
|
||||
|
||||
protected NetworkInfo mNetworkInfo;
|
||||
protected LinkProperties mLinkProperties;
|
||||
protected LinkCapabilities mLinkCapabilities;
|
||||
|
||||
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
|
||||
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
|
||||
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
|
||||
|
||||
public BaseNetworkStateTracker(int networkType) {
|
||||
mNetworkInfo = new NetworkInfo(
|
||||
networkType, -1, ConnectivityManager.getNetworkTypeName(networkType), null);
|
||||
mLinkProperties = new LinkProperties();
|
||||
mLinkCapabilities = new LinkCapabilities();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected Handler getTargetHandler() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
protected final void dispatchStateChanged() {
|
||||
// TODO: include snapshot of other fields when sending
|
||||
mTarget.obtainMessage(EVENT_STATE_CHANGED, getNetworkInfo()).sendToTarget();
|
||||
}
|
||||
|
||||
protected final void dispatchConfigurationChanged() {
|
||||
// TODO: include snapshot of other fields when sending
|
||||
mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, getNetworkInfo()).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void startMonitoring(Context context, Handler target) {
|
||||
mContext = Preconditions.checkNotNull(context);
|
||||
mTarget = Preconditions.checkNotNull(target);
|
||||
startMonitoringInternal();
|
||||
}
|
||||
|
||||
protected abstract void startMonitoringInternal();
|
||||
|
||||
@Override
|
||||
public final NetworkInfo getNetworkInfo() {
|
||||
return new NetworkInfo(mNetworkInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LinkProperties getLinkProperties() {
|
||||
return new LinkProperties(mLinkProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final LinkCapabilities getLinkCapabilities() {
|
||||
return new LinkCapabilities(mLinkCapabilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRadio(boolean turnOn) {
|
||||
// Base tracker doesn't handle radios
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mNetworkInfo.isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserDataEnable(boolean enabled) {
|
||||
// Base tracker doesn't handle enabled flags
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolicyDataEnable(boolean enabled) {
|
||||
// Base tracker doesn't handle enabled flags
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrivateDnsRouteSet() {
|
||||
return mPrivateDnsRouteSet.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void privateDnsRouteSet(boolean enabled) {
|
||||
mPrivateDnsRouteSet.set(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultRouteSet() {
|
||||
return mDefaultRouteSet.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultRouteSet(boolean enabled) {
|
||||
mDefaultRouteSet.set(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTeardownRequested() {
|
||||
return mTeardownRequested.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTeardownRequested(boolean isRequested) {
|
||||
mTeardownRequested.set(isRequested);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDependencyMet(boolean met) {
|
||||
// Base tracker doesn't handle dependencies
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.util.Slog;
|
||||
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -81,7 +83,7 @@ public class SystemService {
|
||||
if (state != null) {
|
||||
return state;
|
||||
} else {
|
||||
throw new IllegalStateException("Service " + service + " in unknown state " + rawState);
|
||||
return State.STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.android.internal.net;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A simple container used to carry information of the ongoing legacy VPN.
|
||||
@@ -27,6 +29,8 @@ import android.os.Parcelable;
|
||||
* @hide
|
||||
*/
|
||||
public class LegacyVpnInfo implements Parcelable {
|
||||
private static final String TAG = "LegacyVpnInfo";
|
||||
|
||||
public static final int STATE_DISCONNECTED = 0;
|
||||
public static final int STATE_INITIALIZING = 1;
|
||||
public static final int STATE_CONNECTING = 2;
|
||||
@@ -66,4 +70,25 @@ public class LegacyVpnInfo implements Parcelable {
|
||||
return new LegacyVpnInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return best matching {@link LegacyVpnInfo} state based on given
|
||||
* {@link NetworkInfo}.
|
||||
*/
|
||||
public static int stateFromNetworkInfo(NetworkInfo info) {
|
||||
switch (info.getDetailedState()) {
|
||||
case CONNECTING:
|
||||
return STATE_CONNECTING;
|
||||
case CONNECTED:
|
||||
return STATE_CONNECTED;
|
||||
case DISCONNECTED:
|
||||
return STATE_DISCONNECTED;
|
||||
case FAILED:
|
||||
return STATE_FAILED;
|
||||
default:
|
||||
Log.w(TAG, "Unhandled state " + info.getDetailedState()
|
||||
+ " ; treating as disconnected");
|
||||
return STATE_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -45,13 +47,14 @@ public class VpnConfig implements Parcelable {
|
||||
}
|
||||
|
||||
public static PendingIntent getIntentForStatusPanel(Context context, VpnConfig config) {
|
||||
Preconditions.checkNotNull(config);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
|
||||
intent.putExtra("config", config);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
|
||||
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
return PendingIntent.getActivity(context, 0, intent, (config == null) ?
|
||||
PendingIntent.FLAG_NO_CREATE : PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
|
||||
public String user;
|
||||
@@ -64,6 +67,7 @@ public class VpnConfig implements Parcelable {
|
||||
public List<String> searchDomains;
|
||||
public PendingIntent configureIntent;
|
||||
public long startTime = -1;
|
||||
public boolean legacy;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
@@ -82,6 +86,7 @@ public class VpnConfig implements Parcelable {
|
||||
out.writeStringList(searchDomains);
|
||||
out.writeParcelable(configureIntent, flags);
|
||||
out.writeLong(startTime);
|
||||
out.writeInt(legacy ? 1 : 0);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<VpnConfig> CREATOR =
|
||||
@@ -99,6 +104,7 @@ public class VpnConfig implements Parcelable {
|
||||
config.searchDomains = in.createStringArrayList();
|
||||
config.configureIntent = in.readParcelable(null);
|
||||
config.startTime = in.readLong();
|
||||
config.legacy = in.readInt() != 0;
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ManageDialog extends AlertActivity implements
|
||||
mDataReceived = (TextView) view.findViewById(R.id.data_received);
|
||||
mDataRowsHidden = true;
|
||||
|
||||
if (mConfig.user.equals(VpnConfig.LEGACY_VPN)) {
|
||||
if (mConfig.legacy) {
|
||||
mAlertParams.mIconId = android.R.drawable.ic_dialog_info;
|
||||
mAlertParams.mTitle = getText(R.string.legacy_title);
|
||||
} else {
|
||||
|
||||
@@ -115,13 +115,15 @@ import java.util.List;
|
||||
* @hide
|
||||
*/
|
||||
public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
private static final String TAG = "ConnectivityService";
|
||||
|
||||
private static final boolean DBG = true;
|
||||
private static final boolean VDBG = false;
|
||||
private static final String TAG = "ConnectivityService";
|
||||
|
||||
private static final boolean LOGD_RULES = false;
|
||||
|
||||
// TODO: create better separation between radio types and network types
|
||||
|
||||
// how long to wait before switching back to a radio's default network
|
||||
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
|
||||
// system property that can override the above value
|
||||
@@ -136,6 +138,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
private boolean mTetheringConfigValid = false;
|
||||
|
||||
private Vpn mVpn;
|
||||
private VpnCallback mVpnCallback = new VpnCallback();
|
||||
|
||||
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
|
||||
private Object mRulesLock = new Object();
|
||||
@@ -328,7 +331,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
this(context, netd, statsService, policyManager, null);
|
||||
}
|
||||
|
||||
public ConnectivityService(Context context, INetworkManagementService netd,
|
||||
public ConnectivityService(Context context, INetworkManagementService netManager,
|
||||
INetworkStatsService statsService, INetworkPolicyManager policyManager,
|
||||
NetworkFactory netFactory) {
|
||||
if (DBG) log("ConnectivityService starting up");
|
||||
@@ -366,7 +369,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
}
|
||||
|
||||
mContext = checkNotNull(context, "missing Context");
|
||||
mNetd = checkNotNull(netd, "missing INetworkManagementService");
|
||||
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
|
||||
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
|
||||
|
||||
try {
|
||||
@@ -506,11 +509,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
|
||||
mTethering.getUpstreamIfaceTypes().length != 0);
|
||||
|
||||
mVpn = new Vpn(mContext, new VpnCallback());
|
||||
mVpn = new Vpn(mContext, mVpnCallback, mNetd);
|
||||
mVpn.startMonitoring(mContext, mTrackerHandler);
|
||||
|
||||
try {
|
||||
mNetd.registerObserver(mTethering);
|
||||
mNetd.registerObserver(mVpn);
|
||||
mNetd.registerObserver(mDataActivityObserver);
|
||||
} catch (RemoteException e) {
|
||||
loge("Error registering observer :" + e);
|
||||
@@ -2238,9 +2241,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
*/
|
||||
public void updateNetworkSettings(NetworkStateTracker nt) {
|
||||
String key = nt.getTcpBufferSizesPropName();
|
||||
String bufferSizes = SystemProperties.get(key);
|
||||
String bufferSizes = key == null ? null : SystemProperties.get(key);
|
||||
|
||||
if (bufferSizes.length() == 0) {
|
||||
if (TextUtils.isEmpty(bufferSizes)) {
|
||||
if (VDBG) log(key + " not found in system properties. Using defaults");
|
||||
|
||||
// Setting to default values so we won't be stuck to previous values
|
||||
@@ -3153,10 +3156,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
||||
* be done whenever a better abstraction is developed.
|
||||
*/
|
||||
public class VpnCallback {
|
||||
|
||||
private VpnCallback() {
|
||||
}
|
||||
|
||||
public void onStateChanged(NetworkInfo info) {
|
||||
// TODO: if connected, release delayed broadcast
|
||||
// TODO: if disconnected, consider kicking off reconnect
|
||||
}
|
||||
|
||||
public void override(List<String> dnsServers, List<String> searchDomains) {
|
||||
if (dnsServers == null) {
|
||||
restore();
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.Manifest.permission.BIND_VPN_SERVICE;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -28,15 +31,21 @@ import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.BaseNetworkStateTracker;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.INetworkManagementEventObserver;
|
||||
import android.net.LocalSocket;
|
||||
import android.net.LocalSocketAddress;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkInfo.DetailedState;
|
||||
import android.os.Binder;
|
||||
import android.os.FileUtils;
|
||||
import android.os.IBinder;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemService;
|
||||
import android.util.Log;
|
||||
@@ -44,7 +53,9 @@ import android.util.Log;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.net.LegacyVpnInfo;
|
||||
import com.android.internal.net.VpnConfig;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.ConnectivityService.VpnCallback;
|
||||
import com.android.server.net.BaseNetworkObserver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
@@ -57,24 +68,63 @@ import libcore.io.IoUtils;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
public class Vpn extends BaseNetworkStateTracker {
|
||||
private static final String TAG = "Vpn";
|
||||
private static final boolean LOGD = true;
|
||||
|
||||
// TODO: create separate trackers for each unique VPN to support
|
||||
// automated reconnection
|
||||
|
||||
private final static String TAG = "Vpn";
|
||||
|
||||
private final static String BIND_VPN_SERVICE =
|
||||
android.Manifest.permission.BIND_VPN_SERVICE;
|
||||
|
||||
private final Context mContext;
|
||||
private final VpnCallback mCallback;
|
||||
|
||||
private String mPackage = VpnConfig.LEGACY_VPN;
|
||||
private String mInterface;
|
||||
private Connection mConnection;
|
||||
private LegacyVpnRunner mLegacyVpnRunner;
|
||||
private PendingIntent mStatusIntent;
|
||||
|
||||
public Vpn(Context context, VpnCallback callback) {
|
||||
public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
|
||||
// TODO: create dedicated TYPE_VPN network type
|
||||
super(ConnectivityManager.TYPE_DUMMY);
|
||||
mContext = context;
|
||||
mCallback = callback;
|
||||
|
||||
try {
|
||||
netService.registerObserver(mObserver);
|
||||
} catch (RemoteException e) {
|
||||
Log.wtf(TAG, "Problem registering observer", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startMonitoringInternal() {
|
||||
// Ignored; events are sent through callbacks for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean teardown() {
|
||||
// TODO: finish migration to unique tracker for each VPN
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reconnect() {
|
||||
// TODO: finish migration to unique tracker for each VPN
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTcpBufferSizesPropName() {
|
||||
return PROP_TCP_BUFFER_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current state, dispaching event to listeners.
|
||||
*/
|
||||
private void updateState(DetailedState detailedState, String reason) {
|
||||
if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
|
||||
mNetworkInfo.setDetailedState(detailedState, reason, null);
|
||||
mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,10 +163,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
// Reset the interface and hide the notification.
|
||||
if (mInterface != null) {
|
||||
jniReset(mInterface);
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
mCallback.restore();
|
||||
hideNotification();
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mCallback.restore();
|
||||
hideNotification();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
mInterface = null;
|
||||
}
|
||||
|
||||
@@ -137,6 +190,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
|
||||
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
|
||||
mPackage = newPackage;
|
||||
updateState(DetailedState.IDLE, "prepare");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,7 +199,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
* interface. The socket is NOT closed by this method.
|
||||
*
|
||||
* @param socket The socket to be bound.
|
||||
* @param name The name of the interface.
|
||||
* @param interfaze The name of the interface.
|
||||
*/
|
||||
public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
@@ -209,6 +263,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
// Configure the interface. Abort if any of these steps fails.
|
||||
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
|
||||
try {
|
||||
updateState(DetailedState.CONNECTING, "establish");
|
||||
String interfaze = jniGetName(tun.getFd());
|
||||
if (jniSetAddresses(interfaze, config.addresses) < 1) {
|
||||
throw new IllegalArgumentException("At least one address must be specified");
|
||||
@@ -229,6 +284,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
mConnection = connection;
|
||||
mInterface = interfaze;
|
||||
} catch (RuntimeException e) {
|
||||
updateState(DetailedState.FAILED, "establish");
|
||||
IoUtils.closeQuietly(tun);
|
||||
throw e;
|
||||
}
|
||||
@@ -239,57 +295,61 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
config.interfaze = mInterface;
|
||||
|
||||
// Override DNS servers and show the notification.
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
mCallback.override(config.dnsServers, config.searchDomains);
|
||||
showNotification(config, label, bitmap);
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mCallback.override(config.dnsServers, config.searchDomains);
|
||||
showNotification(config, label, bitmap);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
// TODO: ensure that contract class eventually marks as connected
|
||||
updateState(DetailedState.AUTHENTICATING, "establish");
|
||||
return tun;
|
||||
}
|
||||
|
||||
// INetworkManagementEventObserver.Stub
|
||||
@Override
|
||||
public void interfaceAdded(String interfaze) {
|
||||
}
|
||||
|
||||
// INetworkManagementEventObserver.Stub
|
||||
@Override
|
||||
public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
|
||||
if (!up && mLegacyVpnRunner != null) {
|
||||
mLegacyVpnRunner.check(interfaze);
|
||||
@Deprecated
|
||||
public synchronized void interfaceStatusChanged(String iface, boolean up) {
|
||||
try {
|
||||
mObserver.interfaceStatusChanged(iface, up);
|
||||
} catch (RemoteException e) {
|
||||
// ignored; target is local
|
||||
}
|
||||
}
|
||||
|
||||
// INetworkManagementEventObserver.Stub
|
||||
@Override
|
||||
public void interfaceLinkStateChanged(String interfaze, boolean up) {
|
||||
}
|
||||
|
||||
// INetworkManagementEventObserver.Stub
|
||||
@Override
|
||||
public synchronized void interfaceRemoved(String interfaze) {
|
||||
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
mCallback.restore();
|
||||
hideNotification();
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
mInterface = null;
|
||||
if (mConnection != null) {
|
||||
mContext.unbindService(mConnection);
|
||||
mConnection = null;
|
||||
} else if (mLegacyVpnRunner != null) {
|
||||
mLegacyVpnRunner.exit();
|
||||
mLegacyVpnRunner = null;
|
||||
private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
|
||||
@Override
|
||||
public void interfaceStatusChanged(String interfaze, boolean up) {
|
||||
synchronized (Vpn.this) {
|
||||
if (!up && mLegacyVpnRunner != null) {
|
||||
mLegacyVpnRunner.check(interfaze);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INetworkManagementEventObserver.Stub
|
||||
@Override
|
||||
public void limitReached(String limit, String interfaze) {
|
||||
}
|
||||
|
||||
public void interfaceClassDataActivityChanged(String label, boolean active) {
|
||||
}
|
||||
@Override
|
||||
public void interfaceRemoved(String interfaze) {
|
||||
synchronized (Vpn.this) {
|
||||
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mCallback.restore();
|
||||
hideNotification();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
mInterface = null;
|
||||
if (mConnection != null) {
|
||||
mContext.unbindService(mConnection);
|
||||
mConnection = null;
|
||||
updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
|
||||
} else if (mLegacyVpnRunner != null) {
|
||||
mLegacyVpnRunner.exit();
|
||||
mLegacyVpnRunner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void enforceControlPermission() {
|
||||
// System user is allowed to control VPN.
|
||||
@@ -326,6 +386,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
}
|
||||
|
||||
private void showNotification(VpnConfig config, String label, Bitmap icon) {
|
||||
mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
|
||||
|
||||
NotificationManager nm = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
@@ -341,15 +403,17 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
.setLargeIcon(icon)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
|
||||
.setContentIntent(mStatusIntent)
|
||||
.setDefaults(0)
|
||||
.setOngoing(true)
|
||||
.getNotification();
|
||||
.build();
|
||||
nm.notify(R.drawable.vpn_connected, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideNotification() {
|
||||
mStatusIntent = null;
|
||||
|
||||
NotificationManager nm = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
@@ -372,25 +436,51 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
* thread, so callers will not be blocked for a long time.
|
||||
*
|
||||
* @param config The parameters to configure the network.
|
||||
* @param raoocn The arguments to be passed to racoon.
|
||||
* @param racoon The arguments to be passed to racoon.
|
||||
* @param mtpd The arguments to be passed to mtpd.
|
||||
*/
|
||||
public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
|
||||
stopLegacyVpn();
|
||||
|
||||
// TODO: move legacy definition to settings
|
||||
config.legacy = true;
|
||||
|
||||
// Prepare for the new request. This also checks the caller.
|
||||
prepare(null, VpnConfig.LEGACY_VPN);
|
||||
updateState(DetailedState.CONNECTING, "startLegacyVpn");
|
||||
|
||||
// Start a new LegacyVpnRunner and we are done!
|
||||
mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
|
||||
mLegacyVpnRunner.start();
|
||||
}
|
||||
|
||||
public synchronized void stopLegacyVpn() {
|
||||
if (mLegacyVpnRunner != null) {
|
||||
mLegacyVpnRunner.exit();
|
||||
mLegacyVpnRunner = null;
|
||||
|
||||
synchronized (LegacyVpnRunner.TAG) {
|
||||
// wait for old thread to completely finish before spinning up
|
||||
// new instance, otherwise state updates can be out of order.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the information of the current ongoing legacy VPN.
|
||||
*/
|
||||
public synchronized LegacyVpnInfo getLegacyVpnInfo() {
|
||||
// Check if the caller is authorized.
|
||||
enforceControlPermission();
|
||||
return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
|
||||
if (mLegacyVpnRunner == null) return null;
|
||||
|
||||
final LegacyVpnInfo info = new LegacyVpnInfo();
|
||||
info.key = mLegacyVpnRunner.mConfig.user;
|
||||
info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
|
||||
if (mNetworkInfo.isConnected()) {
|
||||
info.intent = mStatusIntent;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,8 +497,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
private final String[] mDaemons;
|
||||
private final String[][] mArguments;
|
||||
private final LocalSocket[] mSockets;
|
||||
private final String mOuterInterface;
|
||||
private final LegacyVpnInfo mInfo;
|
||||
|
||||
private long mTimer = -1;
|
||||
|
||||
@@ -416,20 +504,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
super(TAG);
|
||||
mConfig = config;
|
||||
mDaemons = new String[] {"racoon", "mtpd"};
|
||||
// TODO: clear arguments from memory once launched
|
||||
mArguments = new String[][] {racoon, mtpd};
|
||||
mSockets = new LocalSocket[mDaemons.length];
|
||||
mInfo = new LegacyVpnInfo();
|
||||
|
||||
// This is the interface which VPN is running on.
|
||||
mOuterInterface = mConfig.interfaze;
|
||||
|
||||
// Legacy VPN is not a real package, so we use it to carry the key.
|
||||
mInfo.key = mConfig.user;
|
||||
mConfig.user = VpnConfig.LEGACY_VPN;
|
||||
}
|
||||
|
||||
public void check(String interfaze) {
|
||||
if (interfaze.equals(mOuterInterface)) {
|
||||
if (interfaze.equals(mConfig.interfaze)) {
|
||||
Log.i(TAG, "Legacy VPN is going down with " + interfaze);
|
||||
exit();
|
||||
}
|
||||
@@ -441,15 +522,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
for (LocalSocket socket : mSockets) {
|
||||
IoUtils.closeQuietly(socket);
|
||||
}
|
||||
}
|
||||
|
||||
public LegacyVpnInfo getInfo() {
|
||||
// Update the info when VPN is disconnected.
|
||||
if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
|
||||
mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
|
||||
mInfo.intent = null;
|
||||
}
|
||||
return mInfo;
|
||||
updateState(DetailedState.DISCONNECTED, "exit");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -459,6 +532,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
synchronized (TAG) {
|
||||
Log.v(TAG, "Executing");
|
||||
execute();
|
||||
monitorDaemons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,17 +544,17 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
} else if (now - mTimer <= 60000) {
|
||||
Thread.sleep(yield ? 200 : 1);
|
||||
} else {
|
||||
mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
|
||||
updateState(DetailedState.FAILED, "checkpoint");
|
||||
throw new IllegalStateException("Time is up");
|
||||
}
|
||||
}
|
||||
|
||||
private void execute() {
|
||||
// Catch all exceptions so we can clean up few things.
|
||||
boolean initFinished = false;
|
||||
try {
|
||||
// Initialize the timer.
|
||||
checkpoint(false);
|
||||
mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
|
||||
|
||||
// Wait for the daemons to stop.
|
||||
for (String daemon : mDaemons) {
|
||||
@@ -496,6 +570,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
throw new IllegalStateException("Cannot delete the state");
|
||||
}
|
||||
new File("/data/misc/vpn/abort").delete();
|
||||
initFinished = true;
|
||||
|
||||
// Check if we need to restart any of the daemons.
|
||||
boolean restart = false;
|
||||
@@ -503,10 +578,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
restart = restart || (arguments != null);
|
||||
}
|
||||
if (!restart) {
|
||||
mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
|
||||
updateState(DetailedState.DISCONNECTED, "execute");
|
||||
return;
|
||||
}
|
||||
mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
|
||||
updateState(DetailedState.CONNECTING, "execute");
|
||||
|
||||
// Start the daemon with arguments.
|
||||
for (int i = 0; i < mDaemons.length; ++i) {
|
||||
@@ -633,26 +708,53 @@ public class Vpn extends INetworkManagementEventObserver.Stub {
|
||||
showNotification(mConfig, null, null);
|
||||
|
||||
Log.i(TAG, "Connected!");
|
||||
mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
|
||||
mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
|
||||
updateState(DetailedState.CONNECTED, "execute");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.i(TAG, "Aborting", e);
|
||||
exit();
|
||||
} finally {
|
||||
// Kill the daemons if they fail to stop.
|
||||
if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
|
||||
if (!initFinished) {
|
||||
for (String daemon : mDaemons) {
|
||||
SystemService.stop(daemon);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not leave an unstable state.
|
||||
if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
|
||||
mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
|
||||
mInfo.state = LegacyVpnInfo.STATE_FAILED;
|
||||
if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
|
||||
updateState(DetailedState.FAILED, "execute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor the daemons we started, moving to disconnected state if the
|
||||
* underlying services fail.
|
||||
*/
|
||||
private void monitorDaemons() {
|
||||
if (!mNetworkInfo.isConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
Thread.sleep(2000);
|
||||
for (int i = 0; i < mDaemons.length; i++) {
|
||||
if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
|
||||
} finally {
|
||||
for (String daemon : mDaemons) {
|
||||
SystemService.stop(daemon);
|
||||
}
|
||||
|
||||
updateState(DetailedState.DISCONNECTED, "babysit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user