Fix network-feature timeout code.

Track requests independently with seperate timers.  Clean up on expiration
by just stopping that particular request, not immediately restoring the default.

bug: 2127590
This commit is contained in:
Robert Greenwalt
2009-09-27 17:27:04 -07:00
parent 149d5ac149
commit affc3a1c31

View File

@@ -439,6 +439,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return tracker != null && tracker.setRadio(turnOn);
}
/**
* Used to notice when the calling process dies so we can self-expire
*
* Also used to know if the process has cleaned up after itself when
* our auto-expire timer goes off. The timer has a link to an object.
*
*/
private class FeatureUser implements IBinder.DeathRecipient {
int mNetworkType;
String mFeature;
@@ -453,6 +460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -467,11 +475,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
public void binderDied() {
Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
mNetworkType + ", " + mFeature + ", " + mBinder);
stopUsingNetworkFeature(mNetworkType, mFeature, mPid, mUid);
stopUsingNetworkFeature(this, false);
}
public void expire() {
Log.d(TAG, "ConnectivityService FeatureUser expire(" +
mNetworkType + ", " + mFeature + ", " + mBinder);
stopUsingNetworkFeature(this, false);
}
}
// javadoc from interface
public int startUsingNetworkFeature(int networkType, String feature,
IBinder binder) {
if (DBG) {
@@ -483,9 +497,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Phone.APN_REQUEST_FAILED;
}
synchronized (mFeatureUsers) {
mFeatureUsers.add(new FeatureUser(networkType, feature, binder));
}
FeatureUser f = new FeatureUser(networkType, feature, binder);
// TODO - move this into the MobileDataStateTracker
int usedNetworkType = networkType;
@@ -513,10 +525,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Phone.APN_TYPE_NOT_AVAILABLE;
}
if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
// this gets used for per-pid dns when connected
mNetRequestersPids[usedNetworkType].add(currentPid);
synchronized(this) {
mFeatureUsers.add(f);
if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
// this gets used for per-pid dns when connected
mNetRequestersPids[usedNetworkType].add(currentPid);
}
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(
NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
f), getRestoreDefaultNetworkDelay());
if ((ni.isConnectedOrConnecting() == true) &&
!network.isTeardownRequested()) {
@@ -533,22 +552,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// check if the radio in play can make another contact
// assume if cannot for now
// since we have to drop the default on this radio, setup
// an automatic event to switch back
if(mHandler.hasMessages(NetworkStateTracker.
EVENT_RESTORE_DEFAULT_NETWORK, radio) ||
radio.getNetworkInfo().isConnectedOrConnecting()) {
mHandler.removeMessages(NetworkStateTracker.
EVENT_RESTORE_DEFAULT_NETWORK,
radio);
mHandler.sendMessageDelayed(mHandler.obtainMessage(
NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
radio), getRestoreDefaultNetworkDelay());
}
if (DBG) Log.d(TAG, "reconnecting to special network");
network.reconnect();
return Phone.APN_REQUEST_STARTED;
} else {
synchronized(this) {
mFeatureUsers.add(f);
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(
NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
f), getRestoreDefaultNetworkDelay());
return network.startUsingNetworkFeature(feature,
getCallingPid(), getCallingUid());
}
@@ -556,13 +570,43 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Phone.APN_TYPE_NOT_AVAILABLE;
}
// javadoc from interface
public int stopUsingNetworkFeature(int networkType, String feature) {
return stopUsingNetworkFeature(networkType, feature, getCallingPid(),
getCallingUid());
int pid = getCallingPid();
int uid = getCallingUid();
FeatureUser u = null;
boolean found = false;
synchronized(this) {
for (int i = 0; i < mFeatureUsers.size() ; i++) {
u = (FeatureUser)mFeatureUsers.get(i);
if (uid == u.mUid && pid == u.mPid &&
networkType == u.mNetworkType &&
TextUtils.equals(feature, u.mFeature)) {
found = true;
break;
}
}
}
if (found && u != null) {
// stop regardless of how many other time this proc had called start
return stopUsingNetworkFeature(u, true);
} else {
// none found!
return 1;
}
}
private int stopUsingNetworkFeature(int networkType, String feature,
int pid, int uid) {
private int stopUsingNetworkFeature(FeatureUser u, boolean ignoreDups) {
int networkType = u.mNetworkType;
String feature = u.mFeature;
int pid = u.mPid;
int uid = u.mUid;
NetworkStateTracker tracker = null;
boolean callTeardown = false; // used to carry our decision outside of sync block
if (DBG) {
Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
": " + feature);
@@ -572,55 +616,65 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return -1;
}
synchronized (mFeatureUsers) {
for (int i=0; i < mFeatureUsers.size(); i++) {
FeatureUser u = (FeatureUser)mFeatureUsers.get(i);
if (uid == u.mUid && pid == u.mPid &&
networkType == u.mNetworkType &&
TextUtils.equals(feature, u.mFeature)) {
u.unlinkDeathRecipient();
mFeatureUsers.remove(i);
break;
}
}
}
// TODO - move to MobileDataStateTracker
int usedNetworkType = networkType;
if (networkType == ConnectivityManager.TYPE_MOBILE) {
if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
}
}
NetworkStateTracker tracker = mNetTrackers[usedNetworkType];
if(usedNetworkType != networkType) {
Integer currentPid = new Integer(pid);
if (mNetRequestersPids[usedNetworkType].remove(currentPid)) {
reassessPidDns(pid, true);
}
if (mNetRequestersPids[usedNetworkType].size() != 0) {
if (DBG) Log.d(TAG, "not tearing down special network - " +
"others still using it");
// need to link the mFeatureUsers list with the mNetRequestersPids state in this
// sync block
synchronized(this) {
// check if this process still has an outstanding start request
if (!mFeatureUsers.contains(u)) {
return 1;
}
tracker.teardown();
NetworkStateTracker radio = mNetTrackers[networkType];
// Check if we want to revert to the default
if (mHandler.hasMessages(NetworkStateTracker.
EVENT_RESTORE_DEFAULT_NETWORK, radio)) {
mHandler.removeMessages(NetworkStateTracker.
EVENT_RESTORE_DEFAULT_NETWORK, radio);
radio.reconnect();
u.unlinkDeathRecipient();
mFeatureUsers.remove(mFeatureUsers.indexOf(u));
// If we care about duplicate requests, check for that here.
//
// This is done to support the extension of a request - the app
// can request we start the network feature again and renew the
// auto-shutoff delay. Normal "stop" calls from the app though
// do not pay attention to duplicate requests - in effect the
// API does not refcount and a single stop will counter multiple starts.
if (ignoreDups == false) {
for (int i = 0; i < mFeatureUsers.size() ; i++) {
FeatureUser x = (FeatureUser)mFeatureUsers.get(i);
if (x.mUid == u.mUid && x.mPid == u.mPid &&
x.mNetworkType == u.mNetworkType &&
TextUtils.equals(x.mFeature, u.mFeature)) {
return 1;
}
}
}
// TODO - move to MobileDataStateTracker
int usedNetworkType = networkType;
if (networkType == ConnectivityManager.TYPE_MOBILE) {
if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
} else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
}
}
tracker = mNetTrackers[usedNetworkType];
if(usedNetworkType != networkType) {
Integer currentPid = new Integer(pid);
reassessPidDns(pid, true);
mNetRequestersPids[usedNetworkType].remove(currentPid);
if (mNetRequestersPids[usedNetworkType].size() != 0) {
if (DBG) Log.d(TAG, "not tearing down special network - " +
"others still using it");
return 1;
}
callTeardown = true;
}
}
if (callTeardown) {
tracker.teardown();
return 1;
} else {
// do it the old fashioned way
return tracker.stopUsingNetworkFeature(feature, pid, uid);
}
}
@@ -1231,17 +1285,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// fill me in
break;
case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
for (NetworkStateTracker net : mNetTrackers) {
NetworkInfo i = net.getNetworkInfo();
if (i.isConnected() &&
!mNetAttributes[i.getType()].isDefault()) {
if (DBG) {
Log.d(TAG, "tearing down " + i +
" to restore the default network");
}
teardown(net);
}
}
FeatureUser u = (FeatureUser)msg.obj;
u.expire();
break;
}
}