DO NOT MERGE: Move PinningNetworkCallback out to a new NetworkPinner class.
am: 502d7cd551 -s ours
* commit '502d7cd551e8a510f818bbe85a038c4e60913ef7':
DO NOT MERGE: Move PinningNetworkCallback out to a new NetworkPinner class.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8e91d2e..5f1043b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -17,15 +17,18 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -36,22 +39,27 @@
import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Protocol;
-
-import java.net.InetAddress;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.HashMap;
+import com.android.internal.util.MessageUtils;
import libcore.net.event.NetworkEventDispatcher;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Class that answers queries about the state of network connectivity. It also
* notifies applications when network connectivity changes. Get an instance
@@ -73,6 +81,13 @@
public class ConnectivityManager {
private static final String TAG = "ConnectivityManager";
+ private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
+ new Class[]{ConnectivityManager.class}, new String[]{"CALLBACK_"});
+
+ private static final String whatToString(int what) {
+ return sMagicDecoderRing.get(what, Integer.toString(what));
+ }
+
/**
* A change in network connectivity has occurred. A default connection has either
* been established or lost. The NetworkInfo for the affected network is
@@ -335,6 +350,71 @@
public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
/**
+ * Invalid tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ public static final int TETHERING_INVALID = -1;
+
+ /**
+ * Wifi tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_WIFI = 0;
+
+ /**
+ * USB tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_USB = 1;
+
+ /**
+ * Bluetooth tethering type.
+ * @see #startTethering(int, OnStartTetheringCallback, boolean)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_BLUETOOTH = 2;
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering to
+ * enable if any.
+ * @hide
+ */
+ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering for
+ * which to cancel provisioning.
+ * @hide
+ */
+ public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+
+ /**
+ * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+ * provisioning.
+ * @hide
+ */
+ public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+
+ /**
+ * Tells the TetherService to run a provision check now.
+ * @hide
+ */
+ public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+ /**
+ * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+ * which will receive provisioning results. Can be left empty.
+ * @hide
+ */
+ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -511,6 +591,7 @@
private final Context mContext;
private INetworkManagementService mNMService;
+ private INetworkPolicyManager mNPManager;
/**
* Tests if a given integer represents a valid network type.
@@ -661,7 +742,7 @@
try {
return mService.getActiveNetworkInfo();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -681,7 +762,48 @@
try {
return mService.getActiveNetwork();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Configures an always-on VPN connection through a specific application.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should declare a {@link VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param userId The identifier of the user to set an always-on VPN for.
+ * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration.
+
+ * @return {@code true} if the package is set as always-on VPN controller;
+ * {@code false} otherwise.
+ * @hide
+ */
+ public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage) {
+ try {
+ return mService.setAlwaysOnVpnPackage(userId, vpnPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the package name of the currently set always-on VPN application.
+ * If there is no always-on VPN set, or the VPN is provided by the system instead
+ * of by an app, {@code null} will be returned.
+ *
+ * @return Package name of VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set.
+ * @hide
+ */
+ public String getAlwaysOnVpnPackageForUser(int userId) {
+ try {
+ return mService.getAlwaysOnVpnPackage(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -702,7 +824,7 @@
try {
return mService.getActiveNetworkInfoForUid(uid);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -726,7 +848,7 @@
try {
return mService.getNetworkInfo(networkType);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -746,7 +868,7 @@
try {
return mService.getNetworkInfoForNetwork(network);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -767,7 +889,7 @@
try {
return mService.getAllNetworkInfo();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -787,7 +909,7 @@
try {
return mService.getNetworkForType(networkType);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -803,7 +925,7 @@
try {
return mService.getAllNetworks();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -816,7 +938,7 @@
try {
return mService.getDefaultNetworkCapabilitiesForUser(userId);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -835,7 +957,7 @@
try {
return mService.getActiveLinkProperties();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -859,7 +981,7 @@
try {
return mService.getLinkPropertiesForType(networkType);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -876,7 +998,7 @@
try {
return mService.getLinkProperties(network);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -893,7 +1015,25 @@
try {
return mService.getNetworkCapabilities(network);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the URL that should be used for resolving whether a captive portal is present.
+ * 1. This URL should respond with a 204 response to a GET request to indicate no captive
+ * portal is present.
+ * 2. This URL must be HTTP as redirect responses are used to find captive portal
+ * sign-in pages. Captive portals cannot respond to HTTPS requests with redirects.
+ *
+ * @hide
+ */
+ @SystemApi
+ public String getCaptivePortalServerUrl() {
+ try {
+ return mService.getCaptivePortalServerUrl();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1028,13 +1168,25 @@
* Guess what the network request was trying to say so that the resulting
* network is accessible via the legacy (deprecated) API such as
* requestRouteToHost.
- * This means we should try to be fairly preceise about transport and
+ *
+ * This means we should try to be fairly precise about transport and
* capability but ignore things such as networkSpecifier.
* If the request has more than one transport or capability it doesn't
* match the old legacy requests (they selected only single transport/capability)
* so this function cannot map the request to a single legacy type and
* the resulting network will not be available to the legacy APIs.
*
+ * This code is only called from the requestNetwork API (L and above).
+ *
+ * Setting a legacy type causes CONNECTIVITY_ACTION broadcasts, which are expensive
+ * because they wake up lots of apps - see http://b/23350688 . So we currently only
+ * do this for SUPL requests, which are the only ones that we know need it. If
+ * omitting these broadcasts causes unacceptable app breakage, then for backwards
+ * compatibility we can send them:
+ *
+ * if (targetSdkVersion < Build.VERSION_CODES.M) && // legacy API unsupported >= M
+ * targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP)) // requestNetwork not present < L
+ *
* TODO - This should be removed when the legacy APIs are removed.
*/
private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
@@ -1046,6 +1198,14 @@
return TYPE_NONE;
}
+ // Do this only for SUPL, until GnssLocationProvider is fixed. http://b/25876485 .
+ if (!netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ // NOTE: if this causes app breakage, we should not just comment out this early return;
+ // instead, we should make this early return conditional on the requesting app's target
+ // SDK version, as described in the comment above.
+ return TYPE_NONE;
+ }
+
String type = null;
int result = TYPE_NONE;
@@ -1062,7 +1222,7 @@
type = "enableDUN";
result = TYPE_MOBILE_DUN;
} else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- type = "enableSUPL";
+ type = "enableSUPL";
result = TYPE_MOBILE_SUPL;
// back out this hack for mms as they no longer need this and it's causing
// device slowdowns - b/23350688 (note, supl still needs this)
@@ -1173,7 +1333,9 @@
int type = legacyTypeForNetworkCapabilities(netCap);
try {
delay = mService.getRestoreDefaultNetworkDelay(type);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
LegacyRequest l = new LegacyRequest();
l.networkCapabilities = netCap;
l.delay = delay;
@@ -1391,7 +1553,7 @@
try {
return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress());
} catch (RemoteException e) {
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1447,7 +1609,7 @@
try {
return mService.getActiveNetworkQuotaInfo();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1466,7 +1628,9 @@
Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+ " retVal=" + retVal);
return retVal;
- } catch (RemoteException e) { }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
Log.d("ConnectivityManager", "getMobileDataEnabled()- remote exception retVal=false");
return false;
@@ -1527,6 +1691,7 @@
getNetworkManagementService().registerNetworkActivityListener(rl);
mNetworkActivityListeners.put(l, rl);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1544,6 +1709,7 @@
try {
getNetworkManagementService().unregisterNetworkActivityListener(rl);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1559,8 +1725,8 @@
try {
return getNetworkManagementService().isNetworkActive();
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return false;
}
/**
@@ -1591,7 +1757,7 @@
// Have a provisioning app - must only let system apps (which check this app)
// turn on tethering
context.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
+ android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
} else {
int uid = Binder.getCallingUid();
Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
@@ -1634,7 +1800,7 @@
try {
return mService.getTetherableIfaces();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1651,7 +1817,7 @@
try {
return mService.getTetheredIfaces();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1674,7 +1840,7 @@
try {
return mService.getTetheringErroredIfaces();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1688,7 +1854,7 @@
try {
return mService.getTetheredDhcpRanges();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1706,6 +1872,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1715,7 +1886,7 @@
try {
return mService.tether(iface);
} catch (RemoteException e) {
- return TETHER_ERROR_SERVICE_UNAVAIL;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1727,6 +1898,11 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
*
@@ -1736,7 +1912,7 @@
try {
return mService.untether(iface);
} catch (RemoteException e) {
- return TETHER_ERROR_SERVICE_UNAVAIL;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1751,11 +1927,100 @@
*
* {@hide}
*/
+ @SystemApi
public boolean isTetheringSupported() {
try {
return mService.isTetheringSupported();
} catch (RemoteException e) {
- return false;
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ * @hide
+ */
+ @SystemApi
+ public static abstract class OnStartTetheringCallback {
+ /**
+ * Called when tethering has been successfully started.
+ */
+ public void onTetheringStarted() {};
+
+ /**
+ * Called when starting tethering failed.
+ */
+ public void onTetheringFailed() {};
+ }
+
+ /**
+ * Convenient overload for
+ * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
+ * handler to run on the current thread's {@link Looper}.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback) {
+ startTethering(type, showProvisioningUi, callback, null);
+ }
+
+ /**
+ * Runs tether provisioning for the given type if needed and then starts tethering if
+ * the check succeeds. If no carrier provisioning is required for tethering, tethering is
+ * enabled immediately. If provisioning fails, tethering will not be enabled. It also
+ * schedules tether provisioning re-checks if appropriate.
+ *
+ * @param type The type of tethering to start. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
+ * is one. This should be true the first time this function is called and also any time
+ * the user can see this UI. It gives users information from their carrier about the
+ * check failing and how they can sign up for tethering if possible.
+ * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
+ * of the result of trying to tether.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @hide
+ */
+ @SystemApi
+ public void startTethering(int type, boolean showProvisioningUi,
+ final OnStartTetheringCallback callback, Handler handler) {
+ ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ callback.onTetheringStarted();
+ } else {
+ callback.onTetheringFailed();
+ }
+ }
+ };
+ try {
+ mService.startTethering(type, wrappedCallback, showProvisioningUi);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception trying to start tethering.", e);
+ wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
+ }
+ }
+
+ /**
+ * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+ * applicable.
+ *
+ * @param type The type of tethering to stop. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @hide
+ */
+ @SystemApi
+ public void stopTethering(int type) {
+ try {
+ mService.stopTethering(type);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1775,7 +2040,7 @@
try {
return mService.getTetherableUsbRegexs();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1795,7 +2060,7 @@
try {
return mService.getTetherableWifiRegexs();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1815,7 +2080,7 @@
try {
return mService.getTetherableBluetoothRegexs();
} catch (RemoteException e) {
- return new String[0];
+ throw e.rethrowFromSystemServer();
}
}
@@ -1840,7 +2105,7 @@
try {
return mService.setUsbTethering(enable);
} catch (RemoteException e) {
- return TETHER_ERROR_SERVICE_UNAVAIL;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1866,6 +2131,8 @@
public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
/** {@hide} */
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+ /** {@hide} */
+ public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -1883,7 +2150,7 @@
try {
return mService.getLastTetherError(iface);
} catch (RemoteException e) {
- return TETHER_ERROR_SERVICE_UNAVAIL;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1901,6 +2168,7 @@
try {
mService.reportInetCondition(networkType, percentage);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1922,6 +2190,7 @@
mService.reportNetworkConnectivity(network, true);
mService.reportNetworkConnectivity(network, false);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1940,6 +2209,7 @@
try {
mService.reportNetworkConnectivity(network, hasConnectivity);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1959,6 +2229,7 @@
try {
mService.setGlobalProxy(p);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1973,7 +2244,7 @@
try {
return mService.getGlobalProxy();
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -1995,7 +2266,7 @@
try {
return mService.getProxyForNetwork(network);
} catch (RemoteException e) {
- return null;
+ throw e.rethrowFromSystemServer();
}
}
@@ -2030,8 +2301,9 @@
public boolean isNetworkSupported(int networkType) {
try {
return mService.isNetworkSupported(networkType);
- } catch (RemoteException e) {}
- return false;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2051,7 +2323,7 @@
try {
return mService.isActiveNetworkMetered();
} catch (RemoteException e) {
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -2068,7 +2340,7 @@
try {
return mService.updateLockdownVpn();
} catch (RemoteException e) {
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -2087,6 +2359,7 @@
try {
timeOutMs = mService.checkMobileProvisioning(suggestedTimeOutMs);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
return timeOutMs;
}
@@ -2099,8 +2372,8 @@
try {
return mService.getMobileProvisioningUrl();
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return null;
}
/**
@@ -2117,6 +2390,7 @@
try {
mService.setProvisioningNotificationVisible(visible, networkType, action);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2133,6 +2407,7 @@
try {
mService.setAirplaneMode(enable);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2140,14 +2415,18 @@
public void registerNetworkFactory(Messenger messenger, String name) {
try {
mService.registerNetworkFactory(messenger, name);
- } catch (RemoteException e) { }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** {@hide} */
public void unregisterNetworkFactory(Messenger messenger) {
try {
mService.unregisterNetworkFactory(messenger);
- } catch (RemoteException e) { }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2160,7 +2439,7 @@
try {
return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
} catch (RemoteException e) {
- return NETID_UNSET;
+ throw e.rethrowFromSystemServer();
}
}
@@ -2311,9 +2590,11 @@
@Override
public void handleMessage(Message message) {
- if (DBG) Log.d(TAG, "CM callback handler got msg " + message.what);
NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
Network network = (Network) getObject(message, Network.class);
+ if (DBG) {
+ Log.d(TAG, whatToString(message.what) + " for network " + network);
+ }
switch (message.what) {
case CALLBACK_PRECHECK: {
NetworkCallback callback = getCallback(request, "PRECHECK");
@@ -2478,12 +2759,31 @@
sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
}
}
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
return networkCallback.networkRequest;
}
/**
+ * Helper function to requests a network with a particular legacy type.
+ *
+ * This is temporarily public @hide so it can be called by system code that uses the
+ * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
+ * instead network notifications.
+ *
+ * TODO: update said system code to rely on NetworkCallbacks and make this method private.
+ *
+ * @hide
+ */
+ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
+ int timeoutMs, int legacyType) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST,
+ legacyType);
+ }
+
+ /**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
@@ -2513,8 +2813,8 @@
* {@code NetworkCapabilities}.
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
- REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ requestNetwork(request, networkCallback, 0,
+ inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
@@ -2524,7 +2824,7 @@
* This function behaves identically to the non-timedout version, but if a suitable
* network is not found within the given time (in milliseconds) the
* {@link NetworkCallback#unavailable} callback is called. The request must
- * still be released normally by calling {@link releaseNetworkRequest}.
+ * still be released normally by calling {@link unregisterNetworkCallback}.
*
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -2537,12 +2837,15 @@
* this request.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#unavailable} is called.
+ *
+ * TODO: Make timeouts work and then unhide this method.
+ *
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs,
- REQUEST, inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ requestNetwork(request, networkCallback, timeoutMs,
+ inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
}
/**
@@ -2621,7 +2924,9 @@
checkPendingIntent(operation);
try {
mService.pendingRequestForNetwork(request.networkCapabilities, operation);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2639,7 +2944,9 @@
checkPendingIntent(operation);
try {
mService.releasePendingNetworkRequest(operation);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
private void checkPendingIntent(PendingIntent intent) {
@@ -2698,7 +3005,9 @@
checkPendingIntent(operation);
try {
mService.pendingListenForNetwork(request.networkCapabilities, operation);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2716,7 +3025,7 @@
try {
return mService.requestBandwidthUpdate(network);
} catch (RemoteException e) {
- return false;
+ throw e.rethrowFromSystemServer();
}
}
@@ -2736,7 +3045,9 @@
}
try {
mService.releaseNetworkRequest(networkCallback.networkRequest);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2772,7 +3083,9 @@
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
try {
mService.setAcceptUnvalidated(network, accept, always);
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -2783,6 +3096,7 @@
try {
mService.factoryReset();
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2898,7 +3212,7 @@
// Checks whether the calling app can use the legacy routing API (startUsingNetworkFeature,
// stopUsingNetworkFeature, requestRouteToHost), and if not throw UnsupportedOperationException.
- // TODO: convert the existing system users (Tethering, GpsLocationProvider) to the new APIs and
+ // TODO: convert the existing system users (Tethering, GnssLocationProvider) to the new APIs and
// remove these exemptions. Note that this check is not secure, and apps can still access these
// functions by accessing ConnectivityService directly. However, it should be clear that doing
// so is unsupported and may break in the future. http://b/22728205
@@ -2925,4 +3239,72 @@
return NetworkUtils.bindProcessToNetworkForHostResolution(
network == null ? NETID_UNSET : network.netId);
}
+
+ /**
+ * Device is not restricting metered network activity while application is running on
+ * background.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1;
+
+ /**
+ * Device is restricting metered network activity while application is running on background,
+ * but application is allowed to bypass it.
+ * <p>
+ * In this state, application should take action to mitigate metered network access.
+ * For example, a music streaming application should switch to a low-bandwidth bitrate.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2;
+
+ /**
+ * Device is restricting metered network activity while application is running on background.
+ * <p>
+ * In this state, application should not try to use the network while running on background,
+ * because it would be denied.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3;
+
+ /**
+ * A change in the background metered network activity restriction has occurred.
+ * <p>
+ * Applications should call {@link #getRestrictBackgroundStatus()} to check if the restriction
+ * applies to them.
+ * <p>
+ * This is only sent to registered receivers, not manifest receivers.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_RESTRICT_BACKGROUND_CHANGED =
+ "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ RESTRICT_BACKGROUND_STATUS_DISABLED,
+ RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {
+ }
+
+ private INetworkPolicyManager getNetworkPolicyManager() {
+ synchronized (this) {
+ if (mNPManager != null) {
+ return mNPManager;
+ }
+ mNPManager = INetworkPolicyManager.Stub.asInterface(ServiceManager
+ .getService(Context.NETWORK_POLICY_SERVICE));
+ return mNPManager;
+ }
+ }
+
+ /**
+ * Determines if the calling application is subject to metered network restrictions while
+ * running on background.
+ */
+ public @RestrictBackgroundStatus int getRestrictBackgroundStatus() {
+ try {
+ return getNetworkPolicyManager().getRestrictBackgroundByCaller();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d4dd669..1a9c9ea 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -76,6 +76,10 @@
boolean isTetheringSupported();
+ void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+
+ void stopTethering(int type);
+
String[] getTetherableIfaces();
String[] getTetheredIfaces();
@@ -117,6 +121,8 @@
VpnInfo[] getAllVpnInfo();
boolean updateLockdownVpn();
+ boolean setAlwaysOnVpnPackage(int userId, String packageName);
+ String getAlwaysOnVpnPackage(int userId);
int checkMobileProvisioning(int suggestedTimeOutMs);
@@ -165,4 +171,6 @@
in IBinder binder, String srcAddr, int srcPort, String dstAddr);
void stopKeepalive(in Network network, int slot);
+
+ String getCaptivePortalServerUrl();
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c4de4a2..1bb0fbb 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -119,8 +119,12 @@
//
// For one such example of this, see b/18867306.
//
- // TODO: Remove this special case altogether.
- if (before.isIPv4Provisioned() && !after.isIPv4Provisioned()) {
+ // Additionally, losing IPv6 provisioning can result in TCP
+ // connections getting stuck until timeouts fire and other
+ // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
+ // previously dualstack network is deemed a lost of provisioning.
+ if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
+ (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
return ProvisioningChange.LOST_PROVISIONING;
}
return ProvisioningChange.STILL_PROVISIONED;
@@ -667,7 +671,8 @@
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
*/
private boolean hasIPv4AddressOnInterface(String iface) {
- return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+ // mIfaceName can be null.
+ return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
(iface != null && mStackedLinks.containsKey(iface) &&
mStackedLinks.get(iface).hasIPv4Address());
}
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index ea444a4..d5f6321 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -23,6 +23,7 @@
import android.net.RouteInfo;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import junit.framework.TestCase;
import java.net.InetAddress;
@@ -47,6 +48,11 @@
private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ // TODO: replace all calls to NetworkUtils.numericToInetAddress with calls to this method.
+ private InetAddress Address(String addrString) {
+ return NetworkUtils.numericToInetAddress(addrString);
+ }
+
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
// Check implementation of equals(), element by element.
assertTrue(source.isIdenticalInterfaceName(target));
@@ -555,9 +561,13 @@
assertTrue(v46lp.isProvisioned());
assertEquals(ProvisioningChange.STILL_PROVISIONED,
+ LinkProperties.compareProvisioning(v4lp, v46lp));
+ assertEquals(ProvisioningChange.STILL_PROVISIONED,
LinkProperties.compareProvisioning(v6lp, v46lp));
assertEquals(ProvisioningChange.LOST_PROVISIONING,
LinkProperties.compareProvisioning(v46lp, v6lp));
+ assertEquals(ProvisioningChange.LOST_PROVISIONING,
+ LinkProperties.compareProvisioning(v46lp, v4lp));
// Check that losing and gaining a secondary router does not change
// the provisioning status.
@@ -572,6 +582,7 @@
}
@SmallTest
+ @Suppress // Failing.
public void testIsReachable() {
final LinkProperties v4lp = new LinkProperties();
assertFalse(v4lp.isReachable(DNS1));
@@ -645,5 +656,26 @@
assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
assertTrue(v6lp.isReachable(kOnLinkDns));
assertTrue(v6lp.isReachable(DNS6));
+
+ // Check isReachable on stacked links. This requires that the source IP address be assigned
+ // on the interface returned by the route lookup.
+ LinkProperties stacked = new LinkProperties();
+
+ // Can't add a stacked link without an interface name.
+ stacked.setInterfaceName("v4-test0");
+ v6lp.addStackedLink(stacked);
+
+ InetAddress stackedAddress = Address("192.0.0.4");
+ LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addLinkAddress(stackedLinkAddress);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addRoute(new RouteInfo(stackedLinkAddress));
+ assertTrue(stacked.isReachable(stackedAddress));
+ assertTrue(v6lp.isReachable(stackedAddress));
+
+ assertFalse(v6lp.isReachable(DNS1));
+ stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
+ assertTrue(v6lp.isReachable(DNS1));
}
}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index a470de1..a723977 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_DEFAULT;
+import static android.net.NetworkStats.ROAMING_ROAMING;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.SET_DBG_VPN_IN;
@@ -42,62 +45,103 @@
private static final long TEST_START = 1194220800000L;
public void testFindIndex() throws Exception {
- final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
+ final NetworkStats stats = new NetworkStats(TEST_START, 4)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L, 0L,
+ 0L, 10)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ 8L, 11)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 1024L, 8L, 12)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 1024L, 8L,
+ 1024L, 8L, 12);
- assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE));
- assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE));
- assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE));
- assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE));
+ assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
+ assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
}
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L, 0L,
+ 0L, 10)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ 8L, 11)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 1024L, 8L, 12)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 0L, 0L, 10)
+ .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ 8L, 11)
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 1024L, 8L, 12)
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 1024L, 8L,
+ 1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
for (int hint = 0; hint < stats.size(); hint++) {
- assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, hint));
- assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, hint));
- assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, hint));
- assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, hint));
- assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, hint));
- assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, hint));
- assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, hint));
+ assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE,
+ ROAMING_DEFAULT, hint));
+ assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE,
+ ROAMING_DEFAULT, hint));
+ assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE,
+ ROAMING_DEFAULT, hint));
+ assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE,
+ ROAMING_DEFAULT, hint));
+ assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
+ ROAMING_DEFAULT, hint));
+ assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
+ ROAMING_DEFAULT, hint));
+ assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
+ ROAMING_ROAMING, hint));
+ assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE,
+ ROAMING_DEFAULT, hint));
}
}
public void testAddEntryGrow() throws Exception {
- final NetworkStats stats = new NetworkStats(TEST_START, 2);
+ final NetworkStats stats = new NetworkStats(TEST_START, 3);
assertEquals(0, stats.size());
- assertEquals(2, stats.internalSize());
+ assertEquals(3, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 1L, 1L, 2L, 2L, 3);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 2L, 2L, 2L, 2L, 4);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L, 2L,
+ 2L, 3);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2L, 2L, 2L,
+ 2L, 4);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 3L, 3L, 2L,
+ 2L, 5);
- assertEquals(2, stats.size());
- assertEquals(2, stats.internalSize());
+ assertEquals(3, stats.size());
+ assertEquals(3, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 3L, 30L, 4L, 40L, 7);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 4L, 40L, 4L, 40L, 8);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 5L, 50L, 5L, 50L, 10);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 4L, 40L, 4L,
+ 40L, 7);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 5L, 50L, 4L,
+ 40L, 8);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 6L, 60L, 5L,
+ 50L, 10);
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 7L, 70L, 5L,
+ 50L, 11);
- assertEquals(5, stats.size());
- assertTrue(stats.internalSize() >= 5);
+ assertEquals(7, stats.size());
+ assertTrue(stats.internalSize() >= 7);
- assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 1L, 1L, 2L, 2L, 3);
- assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 2L, 2L, 2L, 2L, 4);
- assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 3L, 30L, 4L, 40L, 7);
- assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 4L, 40L, 4L, 40L, 8);
- assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, 5L, 50L, 5L, 50L, 10);
+ assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L,
+ 2L, 2L, 3);
+ assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2L, 2L,
+ 2L, 2L, 4);
+ assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 3L, 3L,
+ 2L, 2L, 5);
+ assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 4L,
+ 40L, 4L, 40L, 7);
+ assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 5L,
+ 50L, 4L, 40L, 8);
+ assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 6L,
+ 60L, 5L, 50L, 10);
+ assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 7L,
+ 70L, 5L, 50L, 11);
}
public void testCombineExisting() throws Exception {
@@ -105,16 +149,23 @@
stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
- stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L, -128L, -1L, -1);
+ stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
+ -128L, -1L, -1);
- assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 384L, 3L, 128L, 1L, 9);
- assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+ assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 384L, 3L,
+ 128L, 1L, 9);
+ assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, ROAMING_DEFAULT, 128L, 1L, 128L,
+ 1L, 2);
// now try combining that should create row
- stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
- assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
- stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3);
- assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 256L, 2L, 256L, 2L, 6);
+ stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
+ 128L, 1L, 3);
+ assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 1L,
+ 128L, 1L, 3);
+ stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
+ 128L, 1L, 3);
+ assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 256L, 2L,
+ 256L, 2L, 6);
}
public void testSubtractIdenticalData() throws Exception {
@@ -129,8 +180,10 @@
final NetworkStats result = after.subtract(before);
// identical data should result in zero delta
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ 0L, 0);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ 0L, 0);
}
public void testSubtractIdenticalRows() throws Exception {
@@ -145,8 +198,10 @@
final NetworkStats result = after.subtract(before);
// expect delta between measurements
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1L, 1L, 2L, 1L, 4);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 4L, 1L, 8);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L, 2L,
+ 1L, 4);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 3L, 1L, 4L,
+ 1L, 8);
}
public void testSubtractNewRows() throws Exception {
@@ -162,9 +217,12 @@
final NetworkStats result = after.subtract(before);
// its okay to have new rows
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0);
- assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ 0L, 0);
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ 0L, 0);
+ assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 1024L, 8L, 20);
}
public void testSubtractMissingRows() throws Exception {
@@ -179,7 +237,8 @@
// should silently drop omitted rows
assertEquals(1, result.size());
- assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 1L, 2L, 3L, 4L, 0);
+ assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L,
+ 2L, 3L, 4L, 0);
assertEquals(4L, result.getTotalBytes());
}
@@ -195,7 +254,7 @@
.addValues(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidSet.getTotalBytes());
- final NetworkStats uidTag = new NetworkStats(TEST_START, 3)
+ final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
.addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
.addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
@@ -203,6 +262,15 @@
.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
.addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
assertEquals(64L, uidTag.getTotalBytes());
+
+ final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L, 0L,
+ 0L);
+ assertEquals(96L, uidRoaming.getTotalBytes());
}
public void testGroupedByIfaceEmpty() throws Exception {
@@ -215,62 +283,98 @@
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, 128L, 8L, 0L, 2L, 20L);
+ .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L, 2L,
+ 20L)
+ .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 2L, 20L)
+ .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L, 2L,
+ 20L);
final NetworkStats grouped = uidStats.groupedByIface();
- assertEquals(2, uidStats.size());
+ assertEquals(3, uidStats.size());
assertEquals(1, grouped.size());
- assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, 256L, 16L, 0L, 4L, 0L);
+ assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 384L, 24L, 0L,
+ 6L, 0L);
}
public void testGroupedByIface() throws Exception {
- final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+ final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 512L, 32L, 0L,
+ 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 4L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 512L, 32L,
+ 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L,
+ 0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
- assertEquals(6, uidStats.size());
+ assertEquals(7, uidStats.size());
assertEquals(2, grouped.size());
- assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, 256L, 16L, 0L, 2L, 0L);
- assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, 1024L, 64L, 0L, 0L, 0L);
+ assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 384L, 24L, 0L,
+ 2L, 0L);
+ assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, ROAMING_ALL, 1024L, 64L,
+ 0L, 0L, 0L);
}
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L,
+ 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L,
+ 0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L,
+ 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L,
+ 0L, 0L);
first.combineAllValues(second);
- assertEquals(3, first.size());
- assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 64L, 0L, 0L, 0L, 0L);
- assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
- assertValues(first, 2, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ assertEquals(4, first.size());
+ assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 64L, 0L, 0L,
+ 0L, 0L);
+ assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 0L,
+ 0L, 0L, 0L);
+ assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 64L, 0L,
+ 0L, 0L, 0L);
+ assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L,
+ 0L, 0L, 0L, 0L);
}
public void testGetTotal() {
- final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+ final NetworkStats stats = new NetworkStats(TEST_START, 7)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 512L, 32L, 0L,
+ 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 4L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 512L, 32L,
+ 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L, 0L,
+ 0L)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L,
+ 0L, 0L);
- assertValues(stats.getTotal(null), 1280L, 80L, 0L, 2L, 20L);
- assertValues(stats.getTotal(null, 100), 1152L, 72L, 0L, 2L, 20L);
+ assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
+ assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L);
assertValues(stats.getTotal(null, 101), 128L, 8L, 0L, 0L, 0L);
final HashSet<String> ifaces = Sets.newHashSet();
@@ -292,8 +396,10 @@
final NetworkStats after = before.withoutUids(new int[] { 100 });
assertEquals(6, before.size());
assertEquals(2, after.size());
- assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
- assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+ assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L,
+ 0L, 0L, 0L);
+ assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L,
+ 0L, 0L);
}
public void testClone() throws Exception {
@@ -351,76 +457,83 @@
assertEquals(21, delta.size());
// tunIface and TEST_IFACE entries are not changed.
- assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE,
+ assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
39605L, 46L, 12259L, 55L, 0L);
- assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
- assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE,
+ assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 0L, 0L,
+ 0L, 0L, 0L);
+ assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
72667L, 197L, 43909L, 241L, 0L);
- assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE,
+ assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
9297L, 17L, 4128L, 21L, 0L);
- assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE,
+ assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
4983L, 10L, 1801L, 12L, 0L);
- assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
- assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1,
+ assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 0L, 0L,
+ 0L, 0L, 0L);
+ assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, ROAMING_DEFAULT,
21691L, 41L, 13820L, 51L, 0L);
- assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L);
- assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L);
+ assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, ROAMING_DEFAULT, 1281L,
+ 2L, 665L, 2L, 0L);
+ assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1685L, 5L,
+ 2070L, 6L, 0L);
// Existing underlying Iface entries are updated
- assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE,
+ assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
44783L, 54L, 13829L, 60L, 0L);
- assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE,
+ assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
0L, 0L, 0L, 0L, 0L);
// VPN underlying Iface entries are updated
- assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
+ assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
28304L, 27L, 1719L, 12L, 0L);
- assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
+ assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
0L, 0L, 0L, 0L, 0L);
// New entries are added for new application's underlying Iface traffic
- assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
- 72667L, 197L, 41872l, 219L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ 72667L, 197L, 41872L, 219L, 0L);
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
9297L, 17L, 3936, 19L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_DEFAULT,
21691L, 41L, 13179L, 46L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1,
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_DEFAULT,
1281L, 2L, 634L, 1L, 0L);
// New entries are added for debug purpose
- assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE,
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
39605L, 46L, 11690, 49, 0);
- assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE,
+ assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
81964, 214, 45808, 238, 0);
- assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE,
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
4983, 10, 1717, 10, 0);
- assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE,
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL,
126552, 270, 59215, 297, 0);
}
private static void assertContains(NetworkStats stats, String iface, int uid, int set,
- int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
- int index = stats.findIndex(iface, uid, set, tag);
+ int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ long operations) {
+ int index = stats.findIndex(iface, uid, set, tag, roaming);
assertTrue(index != -1);
- assertValues(stats, index, iface, uid, set, tag,
+ assertValues(stats, index, iface, uid, set, tag, roaming,
rxBytes, rxPackets, txBytes, txPackets, operations);
}
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
- int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ long operations) {
final NetworkStats.Entry entry = stats.getValues(index, null);
- assertValues(entry, iface, uid, set, tag);
+ assertValues(entry, iface, uid, set, tag, roaming);
assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations);
}
private static void assertValues(
- NetworkStats.Entry entry, String iface, int uid, int set, int tag) {
+ NetworkStats.Entry entry, String iface, int uid, int set, int tag, int roaming) {
assertEquals(iface, entry.iface);
assertEquals(uid, entry.uid);
assertEquals(set, entry.set);
assertEquals(tag, entry.tag);
+ assertEquals(roaming, entry.roaming);
}
private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets,
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index d3dd01a..12a75b8 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import static android.net.NetworkStats.ROAMING_DEFAULT;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
@@ -156,7 +157,7 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long txBytes) {
- final int i = stats.findIndex(iface, uid, set, tag);
+ final int i = stats.findIndex(iface, uid, set, tag, ROAMING_DEFAULT);
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
@@ -164,7 +165,7 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final int i = stats.findIndex(iface, uid, set, tag);
+ final int i = stats.findIndex(iface, uid, set, tag, ROAMING_DEFAULT);
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c80a82e..86040c2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -33,7 +33,7 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.annotation.Nullable;
-import android.app.AlarmManager;
+import android.app.BroadcastOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -72,6 +72,7 @@
import android.net.UidRange;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
@@ -85,7 +86,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
+import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -112,7 +113,6 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
-import com.android.internal.telephony.DctConstants;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -153,11 +153,9 @@
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -166,10 +164,11 @@
implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final boolean LOGD_RULES = false;
+ private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
// TODO: create better separation between radio types and network types
@@ -254,11 +253,6 @@
private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
/**
- * used internally to send a sticky broadcast delayed.
- */
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
-
- /**
* PAC manager has received new port.
*/
private static final int EVENT_PROXY_HAS_CHANGED = 16;
@@ -461,7 +455,7 @@
*/
private class LegacyTypeTracker {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final String TAG = "CSLegacyTypeTracker";
@@ -754,7 +748,7 @@
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- mTethering = new Tethering(mContext, mNetd, statsService, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService);
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -762,6 +756,9 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTING);
intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ intentFilter.addAction(Intent.ACTION_USER_ADDED);
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -963,6 +960,21 @@
}
}
+ private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
+ if (ni == null || !LOGD_BLOCKED_NETWORKINFO) return;
+ boolean removed = false;
+ boolean added = false;
+ synchronized (mBlockedAppUids) {
+ if (ni.getDetailedState() == DetailedState.BLOCKED && mBlockedAppUids.add(uid)) {
+ added = true;
+ } else if (ni.isConnected() && mBlockedAppUids.remove(uid)) {
+ removed = true;
+ }
+ }
+ if (added) log("Returning blocked NetworkInfo to uid=" + uid);
+ else if (removed) log("Returning unblocked NetworkInfo to uid=" + uid);
+ }
+
/**
* Return a filtered {@link NetworkInfo}, potentially marked
* {@link DetailedState#BLOCKED} based on
@@ -973,10 +985,6 @@
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (VDBG) {
- log("returning Blocked NetworkInfo for ifname=" +
- lp.getInterfaceName() + ", uid=" + uid);
- }
}
if (info != null && mLockdownTracker != null) {
info = mLockdownTracker.augmentNetworkInfo(info);
@@ -997,7 +1005,9 @@
enforceAccessPermission();
final int uid = Binder.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
- return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ NetworkInfo ni = getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ maybeLogBlockedNetworkInfo(ni, uid);
+ return ni;
}
@Override
@@ -1372,7 +1382,8 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- if (DBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
+ if (DBG) log("Adding legacy route " + bestRoute +
+ " for UID/PID " + uid + "/" + Binder.getCallingPid());
try {
mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
@@ -1534,6 +1545,7 @@
log("sendStickyBroadcast: action=" + intent.getAction());
}
+ Bundle options = null;
final long ident = Binder.clearCallingIdentity();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(
@@ -1541,6 +1553,10 @@
if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ } else {
+ BroadcastOptions opts = BroadcastOptions.makeBasic();
+ opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+ options = opts.toBundle();
}
final IBatteryStats bs = BatteryStatsService.getService();
try {
@@ -1551,7 +1567,7 @@
}
}
try {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1571,12 +1587,9 @@
// load the global proxy at startup
mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
- // Try bringing up tracker, but if KeyStore isn't ready yet, wait
- // for user to unlock device.
- if (!updateLockdownVpn()) {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
- mContext.registerReceiver(mUserPresentReceiver, filter);
- }
+ // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
+ // for user to unlock device too.
+ updateLockdownVpn();
// Configure whether mobile data is always on.
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
@@ -1586,17 +1599,6 @@
mPermissionMonitor.startMonitoring();
}
- private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Try creating lockdown tracker, since user present usually means
- // unlocked keystore.
- if (updateLockdownVpn()) {
- mContext.unregisterReceiver(this);
- }
- }
- };
-
/**
* Setup data activity tracking for the given network.
*
@@ -2187,7 +2189,7 @@
if (wasDefault) {
mDefaultInetConditionPublished = 0;
}
- notifyIfacesChanged();
+ notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
// by other networks that are already connected. Perhaps that can be done by
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
@@ -2521,11 +2523,6 @@
handleDeprecatedGlobalHttpProxy();
break;
}
- case EVENT_SEND_STICKY_BROADCAST_INTENT: {
- Intent intent = (Intent)msg.obj;
- sendStickyBroadcast(intent);
- break;
- }
case EVENT_PROXY_HAS_CHANGED: {
handleApplyDefaultProxy((ProxyInfo)msg.obj);
break;
@@ -2699,18 +2696,37 @@
// if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
+ @Override
public boolean isTetheringSupported() {
enforceTetherAccessPermission();
int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
- return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
+ return tetherEnabledInSettings && mUserManager.isAdminUser() &&
+ ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceTypes().length != 0);
}
+ @Override
+ public void startTethering(int type, ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ if (!isTetheringSupported()) {
+ receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
+ return;
+ }
+ mTethering.startTethering(type, receiver, showProvisioningUi);
+ }
+
+ @Override
+ public void stopTethering(int type) {
+ ConnectivityManager.enforceTetherChangePermission(mContext);
+ mTethering.stopTethering(type);
+ }
+
// Called when we lose the default network and have no replacement yet.
// This will automatically be cleared after X seconds or a new default network
// becomes CONNECTED, whichever happens first. The timer is started by the
@@ -3227,11 +3243,6 @@
// Tear down existing lockdown if profile was removed
mLockdownEnabled = LockdownVpnTracker.isEnabled();
if (mLockdownEnabled) {
- if (!mKeyStore.isUnlocked()) {
- Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker");
- return false;
- }
-
final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
@@ -3284,6 +3295,76 @@
}
}
+ /**
+ * Sets up or tears down the always-on VPN for user {@param user} as appropriate.
+ *
+ * @return {@code false} in case of errors; {@code true} otherwise.
+ */
+ private boolean updateAlwaysOnVpn(int user) {
+ final String lockdownPackage = getAlwaysOnVpnPackage(user);
+ if (lockdownPackage == null) {
+ return true;
+ }
+
+ // Create an intent to start the VPN service declared in the app's manifest.
+ Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+ serviceIntent.setPackage(lockdownPackage);
+
+ try {
+ return mContext.startServiceAsUser(serviceIntent, UserHandle.of(user)) != null;
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setAlwaysOnVpnPackage(int userId, String packageName) {
+ enforceConnectivityInternalPermission();
+ enforceCrossUserPermission(userId);
+
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (LockdownVpnTracker.isEnabled()) {
+ return false;
+ }
+
+ // If the current VPN package is the same as the new one, this is a no-op
+ final String oldPackage = getAlwaysOnVpnPackage(userId);
+ if (TextUtils.equals(oldPackage, packageName)) {
+ return true;
+ }
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ if (!vpn.setAlwaysOnPackage(packageName)) {
+ return false;
+ }
+ if (!updateAlwaysOnVpn(userId)) {
+ vpn.setAlwaysOnPackage(null);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String getAlwaysOnVpnPackage(int userId) {
+ enforceConnectivityInternalPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return null;
+ }
+ return vpn.getAlwaysOnPackage();
+ }
+ }
+
@Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
// TODO: Remove? Any reason to trigger a provisioning check?
@@ -3540,6 +3621,11 @@
userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
mVpns.put(userId, userVpn);
}
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ updateAlwaysOnVpn(userId);
+ }
}
private void onUserStop(int userId) {
@@ -3553,6 +3639,35 @@
}
}
+ private void onUserAdded(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserAdded(userId);
+ }
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized(mVpns) {
+ final int vpnsSize = mVpns.size();
+ for (int i = 0; i < vpnsSize; i++) {
+ Vpn vpn = mVpns.valueAt(i);
+ vpn.onUserRemoved(userId);
+ }
+ }
+ }
+
+ private void onUserUnlocked(int userId) {
+ // User present may be sent because of an unlock, which might mean an unlocked keystore.
+ if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ updateAlwaysOnVpn(userId);
+ }
+ }
+
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3564,6 +3679,12 @@
onUserStart(userId);
} else if (Intent.ACTION_USER_STOPPING.equals(action)) {
onUserStop(userId);
+ } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ onUserUnlocked(userId);
}
}
};
@@ -3914,6 +4035,9 @@
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
new HashMap<Messenger, NetworkAgentInfo>();
+ @GuardedBy("mBlockedAppUids")
+ private final HashSet<Integer> mBlockedAppUids = new HashSet();
+
// Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
private final NetworkRequest mDefaultRequest;
@@ -3999,7 +4123,7 @@
}
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
- notifyIfacesChanged();
+ notifyIfacesChangedForNetworkStats();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
@@ -4623,7 +4747,11 @@
notifyLockdownVpn(networkAgent);
if (oldInfo != null && oldInfo.getState() == state) {
- if (VDBG) log("ignoring duplicate network state non-change");
+ if (oldInfo.isRoaming() != newInfo.isRoaming()) {
+ if (VDBG) log("roaming status changed, notifying NetworkStatsService");
+ notifyIfacesChangedForNetworkStats();
+ } else if (VDBG) log("ignoring duplicate network state non-change");
+ // In either case, no further work should be needed.
return;
}
if (DBG) {
@@ -4653,7 +4781,7 @@
}
networkAgent.created = true;
updateLinkProperties(networkAgent, null);
- notifyIfacesChanged();
+ notifyIfacesChangedForNetworkStats();
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
scheduleUnvalidatedPrompt(networkAgent);
@@ -4817,9 +4945,10 @@
}
/**
- * Notify other system services that set of active ifaces has changed.
+ * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
+ * properties tracked by NetworkStatsService on an active iface has changed.
*/
- private void notifyIfacesChanged() {
+ private void notifyIfacesChangedForNetworkStats() {
try {
mStatsService.forceUpdateIfaces();
} catch (Exception ignored) {
@@ -4853,12 +4982,17 @@
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- notifyIfacesChanged();
+ notifyIfacesChangedForNetworkStats();
}
return success;
}
@Override
+ public String getCaptivePortalServerUrl() {
+ return NetworkMonitor.getCaptivePortalServerUrl(mContext);
+ }
+
+ @Override
public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
IBinder binder, String srcAddr, int srcPort, String dstAddr) {
enforceKeepalivePermission();
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 90c9ddf..9e1f6b8 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -60,7 +60,7 @@
public class KeepaliveTracker {
private static final String TAG = "KeepaliveTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 384c620..b4c71c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -252,10 +252,10 @@
}
public String toString() {
- return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" +
- network + "} lp{" +
- linkProperties + "} nc{" +
- networkCapabilities + "} Score{" + getCurrentScore() + "} " +
+ return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
+ "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
+ "lp{" + linkProperties + "} " +
+ "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
"created{" + created + "} lingering{" + lingering + "} " +
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index debda14..22cefd1 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -54,7 +54,7 @@
*/
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean SYSTEM = true;
private static final boolean NETWORK = false;
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 7fe8158..69f12eb 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -59,12 +59,14 @@
import android.os.MessageQueue;
import android.os.Messenger;
import android.os.MessageQueue.IdleHandler;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.LogPrinter;
+import com.android.internal.util.WakeupMessage;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.net.NetworkPinner;
@@ -505,6 +507,35 @@
}
}
+ private class FakeWakeupMessage extends WakeupMessage {
+ private static final int UNREASONABLY_LONG_WAIT = 1000;
+
+ public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
+ super(context, handler, cmdName, cmd);
+ }
+
+ @Override
+ public void schedule(long when) {
+ long delayMs = when - SystemClock.elapsedRealtime();
+ if (delayMs < 0) delayMs = 0;
+ if (delayMs > UNREASONABLY_LONG_WAIT) {
+ fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
+ "ms into the future: " + delayMs);
+ }
+ mHandler.sendEmptyMessageDelayed(mCmd, delayMs);
+ }
+
+ @Override
+ public void cancel() {
+ mHandler.removeMessages(mCmd);
+ }
+
+ @Override
+ public void onAlarm() {
+ throw new AssertionError("Should never happen. Update this fake.");
+ }
+ }
+
// NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
private class WrappedNetworkMonitor extends NetworkMonitor {
// HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
@@ -519,6 +550,12 @@
protected int isCaptivePortal() {
return gen204ProbeResult;
}
+
+ @Override
+ protected WakeupMessage makeWakeupMessage(
+ Context context, Handler handler, String cmdName, int cmd) {
+ return new FakeWakeupMessage(context, handler, cmdName, cmd);
+ }
}
private class WrappedConnectivityService extends ConnectivityService {
@@ -596,10 +633,10 @@
int delays = 0;
while (!criteria.get()) {
try {
- Thread.sleep(100);
+ Thread.sleep(50);
} catch (InterruptedException e) {
}
- if (++delays == 5) fail();
+ if (++delays == 10) fail();
}
}
@@ -615,6 +652,8 @@
public void setUp() throws Exception {
super.setUp();
+ NetworkMonitor.SetDefaultLingerTime(120);
+
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
if (Looper.myLooper() == null) {
@@ -696,8 +735,6 @@
@LargeTest
public void testLingering() throws Exception {
- // Decrease linger timeout to the minimum allowed by AlarmManagerService.
- NetworkMonitor.SetDefaultLingerTime(5000);
verifyNoNetwork();
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
@@ -724,10 +761,8 @@
assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
// Test cellular linger timeout.
- try {
- Thread.sleep(6000);
- } catch (InterruptedException e) {
- }
+ waitFor(new Criteria() {
+ public boolean get() { return mCm.getAllNetworks().length == 1; } });
verifyActiveNetwork(TRANSPORT_WIFI);
assertEquals(1, mCm.getAllNetworks().length);
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
@@ -1560,6 +1595,12 @@
callback3.expectStopped();
}
+ @SmallTest
+ public void testGetCaptivePortalServerUrl() throws Exception {
+ String url = mCm.getCaptivePortalServerUrl();
+ assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
+ }
+
private static class TestNetworkPinner extends NetworkPinner {
public static boolean awaitPin(int timeoutMs) {
synchronized(sLock) {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
new file mode 100644
index 0000000..bb8f9d1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 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.net;
+
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.AppOpsManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.telephony.TelephonyManager;
+
+import com.android.server.LocalServices;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class NetworkStatsAccessTest extends TestCase {
+ private static final String TEST_PKG = "com.example.test";
+ private static final int TEST_UID = 12345;
+
+ @Mock private Context mContext;
+ @Mock private DevicePolicyManagerInternal mDpmi;
+ @Mock private TelephonyManager mTm;
+ @Mock private AppOpsManager mAppOps;
+
+ // Hold the real service so we can restore it when tearing down the test.
+ private DevicePolicyManagerInternal mSystemDpmi;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi);
+
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm);
+ when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi);
+ super.tearDown();
+ }
+
+ public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception {
+ setHasCarrierPrivileges(true);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_isDeviceOwner() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(true);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_isProfileOwner() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(true);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_deniedAppOpsBit() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ private void setHasCarrierPrivileges(boolean hasPrivileges) {
+ when(mTm.checkCarrierPrivilegesForPackage(TEST_PKG)).thenReturn(
+ hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+ : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ }
+
+ private void setIsDeviceOwner(boolean isOwner) {
+ when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
+ .thenReturn(isOwner);
+ }
+
+ private void setIsProfileOwner(boolean isOwner) {
+ when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
+ .thenReturn(isOwner);
+ }
+
+ private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
+ when(mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
+ .thenReturn(appOpsMode);
+ when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
+ hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+
+ private void setHasReadHistoryPermission(boolean hasPermission) {
+ when(mContext.checkCallingOrSelfPermission(permission.READ_NETWORK_USAGE_HISTORY))
+ .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
index 1a6c289..6026644 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -16,6 +16,7 @@
package com.android.server.net;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
@@ -24,9 +25,14 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import android.content.res.Resources;
+import android.net.NetworkIdentity;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
+import android.os.Process;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.frameworks.servicestests.R;
@@ -68,7 +74,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L);
+ 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -77,12 +83,12 @@
// clear structure completely
collection.reset();
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 0L, 0L, 0L, 0L);
+ 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L);
+ 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
}
public void testReadLegacyUid() throws Exception {
@@ -94,7 +100,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L);
+ 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -103,12 +109,12 @@
// clear structure completely
collection.reset();
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 0L, 0L, 0L, 0L);
+ 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L);
+ 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
}
public void testReadLegacyUidTags() throws Exception {
@@ -151,6 +157,66 @@
assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
}
+ public void testAccessLevels() throws Exception {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ final NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_IMSI, null, false));
+
+ int myUid = Process.myUid();
+ int otherUidInSameUser = Process.myUid() + 1;
+ int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE;
+
+ // Record one entry for the current UID.
+ entry.rxBytes = 32;
+ collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS,
+ entry);
+
+ // Record one entry for another UID in this user.
+ entry.rxBytes = 64;
+ collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Record one entry for the system UID.
+ entry.rxBytes = 128;
+ collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Record one entry for a UID in a different user.
+ entry.rxBytes = 256;
+ collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Verify the set of relevant UIDs for each access level.
+ MoreAsserts.assertEquals(new int[] { myUid },
+ collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT));
+ MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
+ collection.getRelevantUids(NetworkStatsAccess.Level.USER));
+ MoreAsserts.assertEquals(
+ new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser },
+ collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
+
+ // Verify security check in getHistory.
+ assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), myUid, SET_DEFAULT,
+ TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT));
+ try {
+ collection.getHistory(buildTemplateMobileAll(TEST_IMSI), otherUidInSameUser,
+ SET_DEFAULT, TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT);
+ fail("Should have thrown SecurityException for accessing different UID");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ // Verify appropriate aggregation in getSummary.
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0,
+ NetworkStatsAccess.Level.DEFAULT);
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0,
+ NetworkStatsAccess.Level.USER);
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0,
+ 0, NetworkStatsAccess.Level.DEVICE);
+ }
+
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
@@ -170,16 +236,19 @@
}
private static void assertSummaryTotal(NetworkStatsCollection collection,
- NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ @NetworkStatsAccess.Level int accessLevel) {
final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel)
+ .getTotal(null);
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
}
private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE).getTotalIncludingTags(null);
+ template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE)
+ .getTotalIncludingTags(null);
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
new file mode 100644
index 0000000..b9e9aa9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2016 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.net;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.when;
+
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.ROAMING_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import android.app.usage.NetworkStatsManager;
+import android.net.DataUsageRequest;
+import android.net.NetworkIdentity;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Process;
+
+import android.os.ConditionVariable;
+import android.os.Looper;
+import android.os.Messenger;
+import android.os.Message;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+
+import com.android.internal.net.VpnInfo;
+import com.android.server.net.NetworkStatsService;
+import com.android.server.net.NetworkStatsServiceTest.IdleableHandlerThread;
+import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link NetworkStatsObservers}.
+ */
+public class NetworkStatsObserversTest extends TestCase {
+ private static final String TEST_IFACE = "test0";
+ private static final String TEST_IFACE2 = "test1";
+ private static final long TEST_START = 1194220800000L;
+
+ private static final String IMSI_1 = "310004";
+ private static final String IMSI_2 = "310260";
+ private static final String TEST_SSID = "AndroidAP";
+
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
+ private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
+ private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
+
+ private static final int UID_RED = UserHandle.PER_USER_RANGE + 1;
+ private static final int UID_BLUE = UserHandle.PER_USER_RANGE + 2;
+ private static final int UID_GREEN = UserHandle.PER_USER_RANGE + 3;
+ private static final int UID_ANOTHER_USER = 2 * UserHandle.PER_USER_RANGE + 4;
+
+ private static final long WAIT_TIMEOUT = 500; // 1/2 sec
+ private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES;
+ private static final long BASE_BYTES = 7 * MB_IN_BYTES;
+ private static final int INVALID_TYPE = -1;
+
+ private static final int[] NO_UIDS = null;
+ private static final VpnInfo[] VPN_INFO = new VpnInfo[0];
+
+ private long mElapsedRealtime;
+
+ private IdleableHandlerThread mObserverHandlerThread;
+ private Handler mObserverNoopHandler;
+
+ private LatchedHandler mHandler;
+ private ConditionVariable mCv;
+
+ private NetworkStatsObservers mStatsObservers;
+ private Messenger mMessenger;
+ private ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
+ private ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
+
+ @Mock private IBinder mockBinder;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ mObserverHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mObserverHandlerThread.start();
+ final Looper observerLooper = mObserverHandlerThread.getLooper();
+ mStatsObservers = new NetworkStatsObservers() {
+ @Override
+ protected Looper getHandlerLooperLocked() {
+ return observerLooper;
+ }
+ };
+
+ mCv = new ConditionVariable();
+ mHandler = new LatchedHandler(Looper.getMainLooper(), mCv);
+ mMessenger = new Messenger(mHandler);
+
+ mActiveIfaces = new ArrayMap<>();
+ mActiveUidIfaces = new ArrayMap<>();
+ }
+
+ public void testRegister_thresholdTooLow_setsDefaultThreshold() throws Exception {
+ long thresholdTooLowBytes = 1L;
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateWifi };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, thresholdTooLowBytes);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+ }
+
+ public void testRegister_highThreshold_accepted() throws Exception {
+ long highThresholdBytes = 2 * THRESHOLD_BYTES;
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateWifi };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, highThresholdBytes);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(highThresholdBytes, request.thresholdInBytes);
+ }
+
+ public void testRegister_twoRequests_twoIds() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateWifi };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request1 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request1.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request1.templates));
+ assertNull(request1.uids);
+ assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes);
+
+ DataUsageRequest request2 = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request2.requestId > request1.requestId);
+ assertTrue(Arrays.deepEquals(templates, request2.templates));
+ assertNull(request2.uids);
+ assertEquals(THRESHOLD_BYTES, request2.thresholdInBytes);
+ }
+
+ public void testRegister_defaultAccess_otherUids_securityException() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ int[] uids = new int[] { UID_RED, UID_BLUE, UID_GREEN };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, uids, THRESHOLD_BYTES);
+
+ try {
+ mStatsObservers.register(inputRequest, mMessenger, mockBinder, UID_RED,
+ NetworkStatsAccess.Level.DEFAULT);
+ fail("Should have denied access");
+ } catch (SecurityException expected) {}
+ }
+
+ public void testRegister_userAccess_otherUidsSameUser()
+ throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ int[] uids = new int[] { UID_RED, UID_BLUE, UID_GREEN };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, uids, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.USER);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertTrue(Arrays.equals(uids, request.uids));
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+ }
+
+ public void testRegister_defaultAccess_sameUid() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ int[] uids = new int[] { UID_RED };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, uids, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.DEFAULT);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertTrue(Arrays.equals(uids, request.uids));
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+ }
+
+ public void testUnregister_unknownRequest_noop() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateWifi };
+ DataUsageRequest unknownRequest = new DataUsageRequest(
+ 123456 /* id */, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ mStatsObservers.unregister(unknownRequest, UID_RED);
+ }
+
+ public void testUnregister_knownRequest_releasesCaller() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+ Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+
+ mStatsObservers.unregister(request, Process.SYSTEM_UID);
+ waitForObserverToIdle();
+
+ Mockito.verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+ }
+
+ public void testUnregister_knownRequest_invalidUid_doesNotUnregister() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+ Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
+
+ mStatsObservers.unregister(request, UID_BLUE);
+ waitForObserverToIdle();
+
+ Mockito.verifyZeroInteractions(mockBinder);
+ }
+
+ public void testUpdateStats_initialSample_doesNotNotify() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ NetworkStats uidSnapshot = null;
+
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_belowThreshold_doesNotNotify() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ NetworkStats uidSnapshot = null;
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ mCv.block(WAIT_TIMEOUT);
+ assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_aboveThresholdNetwork_notifies() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ NetworkStats uidSnapshot = null;
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+ BASE_BYTES + THRESHOLD_BYTES, 22L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_aboveThresholdMultipleNetwork_notifies() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1, sTemplateImsi2 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.DEVICESUMMARY);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet1 = new NetworkIdentitySet();
+ identSet1.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveIfaces.put(TEST_IFACE, identSet1);
+
+ NetworkIdentitySet identSet2 = new NetworkIdentitySet();
+ identSet2.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_2, null /* networkId */, false /* roaming */));
+ mActiveIfaces.put(TEST_IFACE2, identSet2);
+
+ // Baseline
+ NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L)
+ .addIfaceValues(TEST_IFACE2, BASE_BYTES + 1234L, 18L, BASE_BYTES, 12L);
+ NetworkStats uidSnapshot = null;
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta - traffic on IMSI2
+ xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
+ .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L)
+ .addIfaceValues(TEST_IFACE2, BASE_BYTES + THRESHOLD_BYTES, 22L,
+ BASE_BYTES + THRESHOLD_BYTES, 24L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_aboveThresholdUid_notifies() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ int[] uids = new int[] { UID_RED, UID_BLUE, UID_GREEN };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, uids, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertTrue(Arrays.equals(uids,request.uids));
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveUidIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = null;
+ NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_defaultAccess_noUid_notifiesSameUid() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.DEFAULT);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveUidIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = null;
+ NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_defaultAccess_noUid_usageOtherUid_doesNotNotify() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_BLUE, NetworkStatsAccess.Level.DEFAULT);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveUidIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = null;
+ NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_userAccess_usageSameUser_notifies() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_BLUE, NetworkStatsAccess.Level.USER);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveUidIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = null;
+ NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ }
+
+ public void testUpdateStats_userAccess_usageAnotherUser_doesNotNotify() throws Exception {
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1 };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, NO_UIDS, THRESHOLD_BYTES);
+
+ DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder,
+ UID_RED, NetworkStatsAccess.Level.USER);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
+
+ NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ IMSI_1, null /* networkId */, false /* roaming */));
+ mActiveUidIfaces.put(TEST_IFACE, identSet);
+
+ // Baseline
+ NetworkStats xtSnapshot = null;
+ NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+
+ // Delta
+ uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
+ .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
+ mStatsObservers.updateStats(
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
+ VPN_INFO, TEST_START);
+ waitForObserverToIdle();
+
+ assertTrue(mCv.block(WAIT_TIMEOUT));
+ assertEquals(INVALID_TYPE, mHandler.mLastMessageType);
+ }
+
+ private void waitForObserverToIdle() {
+ // Send dummy message to make sure that any previous message has been handled
+ mHandler.sendMessage(mHandler.obtainMessage(-1));
+ mObserverHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
similarity index 65%
rename from services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
rename to services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
index c12f978..4f6c7b9 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
@@ -14,15 +14,17 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.net;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_DEFAULT;
+import static android.net.NetworkStats.ROAMING_ROAMING;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
@@ -41,6 +43,7 @@
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.easymock.EasyMock.anyInt;
import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -49,9 +52,13 @@
import static org.easymock.EasyMock.isA;
import android.app.AlarmManager;
+import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
import android.content.Intent;
+import android.net.DataUsageRequest;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsSession;
@@ -62,7 +69,17 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.INetworkManagementService;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Messenger;
+import android.os.MessageQueue;
+import android.os.MessageQueue.IdleHandler;
+import android.os.Message;
+import android.os.PowerManager;
import android.os.WorkSource;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
@@ -70,6 +87,8 @@
import android.test.suitebuilder.annotation.Suppress;
import android.util.TrustedTime;
+import com.android.internal.net.VpnInfo;
+import com.android.server.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -80,9 +99,15 @@
import org.easymock.EasyMock;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Tests for {@link NetworkStatsService}.
+ *
+ * TODO: This test is really brittle, largely due to overly-strict use of Easymock.
+ * Rewrite w/ Mockito.
*/
@LargeTest
public class NetworkStatsServiceTest extends AndroidTestCase {
@@ -104,16 +129,20 @@
private static final int UID_BLUE = 1002;
private static final int UID_GREEN = 1003;
+ private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs
+ private static final int INVALID_TYPE = -1;
+
private long mElapsedRealtime;
private BroadcastInterceptingContext mServiceContext;
private File mStatsDir;
private INetworkManagementService mNetManager;
- private IAlarmManager mAlarmManager;
private TrustedTime mTime;
private NetworkStatsSettings mSettings;
private IConnectivityManager mConnManager;
+ private IdleableHandlerThread mHandlerThread;
+ private Handler mHandler;
private NetworkStatsService mService;
private INetworkStatsSession mSession;
@@ -130,20 +159,34 @@
}
mNetManager = createMock(INetworkManagementService.class);
- mAlarmManager = createMock(IAlarmManager.class);
+
+ // TODO: Mock AlarmManager when migrating this test to Mockito.
+ AlarmManager alarmManager = (AlarmManager) mServiceContext
+ .getSystemService(Context.ALARM_SERVICE);
mTime = createMock(TrustedTime.class);
mSettings = createMock(NetworkStatsSettings.class);
mConnManager = createMock(IConnectivityManager.class);
+ PowerManager powerManager = (PowerManager) mServiceContext.getSystemService(
+ Context.POWER_SERVICE);
+ PowerManager.WakeLock wakeLock =
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
mService = new NetworkStatsService(
- mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir, mSettings);
+ mServiceContext, mNetManager, alarmManager, wakeLock, mTime,
+ TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
+ mStatsDir, getBaseDir(mStatsDir));
+ mHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mHandlerThread.start();
+ Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
+ mHandler = new Handler(mHandlerThread.getLooper(), callback);
+ mService.setHandler(mHandler, callback);
mService.bindConnectivityManager(mConnManager);
mElapsedRealtime = 0L;
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
@@ -170,7 +213,6 @@
mStatsDir = null;
mNetManager = null;
- mAlarmManager = null;
mTime = null;
mSettings = null;
mConnManager = null;
@@ -190,9 +232,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -208,7 +251,7 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
@@ -225,7 +268,7 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
@@ -244,9 +287,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -272,13 +316,14 @@
mService.incrementOperationCount(UID_RED, 0xFAAD, 6);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, 512L, 4L, 256L, 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, 512L, 4L, 256L, 2L, 6);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_DEFAULT, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_DEFAULT, 512L, 4L, 256L, 2L,
+ 6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
verifyAndReset();
@@ -295,7 +340,6 @@
// boot through serviceReady() again
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
@@ -313,8 +357,9 @@
// after systemReady(), we should have historical stats loaded again
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, 512L, 4L, 256L, 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, 512L, 4L, 256L, 2L, 6);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_DEFAULT, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_DEFAULT, 512L, 4L, 256L, 2L,
+ 6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
verifyAndReset();
@@ -335,9 +380,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// modify some number on wifi, and trigger poll event
@@ -350,7 +396,7 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL);
@@ -368,7 +414,7 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify identical stats, but spread across 4 buckets now
history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL);
@@ -387,9 +433,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic on first network
@@ -407,7 +454,7 @@
mService.incrementOperationCount(UID_RED, 0xF00D, 10);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
@@ -429,10 +476,11 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ mService.forceUpdateIfaces();
+ forcePollAndWaitForIdle();
verifyAndReset();
// create traffic on second network
@@ -451,7 +499,7 @@
mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify original history still intact
assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
@@ -475,9 +523,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic
@@ -496,7 +545,7 @@
mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0);
@@ -544,9 +593,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic
@@ -562,7 +612,7 @@
mService.incrementOperationCount(UID_RED, 0xF00D, 5);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
@@ -578,10 +628,11 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ mService.forceUpdateIfaces();
+ forcePollAndWaitForIdle();
verifyAndReset();
// create traffic on second network
@@ -599,7 +650,7 @@
mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify that ALL_MOBILE template combines both
assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
@@ -615,9 +666,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic for two apps
@@ -634,7 +686,7 @@
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 1);
@@ -653,22 +705,26 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// first verify entire history present
NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(3, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 1);
- assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 50L, 5L,
+ 50L, 5L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 10L, 1L, 10L,
+ 1L, 1);
+ assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2048L, 16L,
+ 1024L, 8L, 0);
// now verify that recent history only contains one uid
final long currentTime = currentTimeMillis();
stats = mSession.getSummaryForAllUid(
sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
assertEquals(1, stats.size());
- assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0);
+ assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ 512L, 4L, 0);
verifyAndReset();
}
@@ -681,9 +737,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some initial traffic
@@ -699,7 +756,7 @@
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
@@ -721,7 +778,7 @@
mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// test that we combined correctly
assertUidTotal(sTemplateWifi, UID_RED, 160L, 4L, 160L, 4L, 2);
@@ -730,10 +787,61 @@
final NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(4, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ 128L, 2L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ 1L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 2L,
+ 32L, 2L, 1);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, ROAMING_DEFAULT, 1L, 1L, 1L,
+ 1L, 1);
+
+ verifyAndReset();
+ }
+
+ public void testRoaming() throws Exception {
+ // pretend that network comes online
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_1, true /* isRoaming */));
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
+
+ replay();
+ mService.forceUpdateIfaces();
+ verifyAndReset();
+
+ // Create some traffic
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ // Note that all traffic from NetworkManagementService is tagged as ROAMING_DEFAULT, because
+ // roaming isn't tracked at that layer. We layer it on top by inspecting the iface
+ // properties.
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ 128L, 2L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ 1L, 0L));
+ expectNetworkStatsPoll();
+
+ replay();
+ forcePollAndWaitForIdle();
+
+ // verify service recorded history
+ assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0);
+
+ // verify entire history present
+ final NetworkStats stats = mSession.getSummaryForAllUid(
+ sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(2, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 2L,
+ 128L, 2L, 0);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING, 64L, 1L, 64L,
+ 1L, 0);
verifyAndReset();
}
@@ -746,9 +854,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some tethering traffic
@@ -768,7 +877,7 @@
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
// verify service recorded history
assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
@@ -778,63 +887,283 @@
}
- public void testReportXtOverDev() throws Exception {
- // bring mobile network online
+ public void testRegisterDataUsageCallback_network() throws Exception {
+ // pretend that wifi network comes online; service should ask about full
+ // network state, and poll any existing interfaces before updating.
expectCurrentTime();
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1));
+ expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
+
+ // verify service has empty history for wifi
+ assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
verifyAndReset();
- // create some traffic, but only for DEV, and across 1.5 buckets
- incrementCurrentTime(90 * MINUTE_IN_MILLIS);
+ String callingPackage = "the.calling.package";
+ long thresholdInBytes = 1L; // very small; should be overriden by framework
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateWifi };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, null /* uids */, thresholdInBytes);
+
+ // Create a messenger that waits for callback activity
+ ConditionVariable cv = new ConditionVariable(false);
+ LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv);
+ Messenger messenger = new Messenger(latchedHandler);
+
+ // Allow binder to connect
+ IBinder mockBinder = createMock(IBinder.class);
+ mockBinder.linkToDeath((IBinder.DeathRecipient) anyObject(), anyInt());
+ EasyMock.replay(mockBinder);
+
+ // Force poll
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 6000L, 60L, 3000L, 30L));
- expectNetworkStatsSummaryXt(buildEmptyStats());
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
+ replay();
+
+ // Register and verify request and that binder was called
+ DataUsageRequest request =
+ mService.registerDataUsageCallback(callingPackage, inputRequest,
+ messenger, mockBinder);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertNull(request.uids);
+ long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB
+ assertEquals(minThresholdInBytes, request.thresholdInBytes);
+
+ // Send dummy message to make sure that any previous message has been handled
+ mHandler.sendMessage(mHandler.obtainMessage(-1));
+ mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+
+ verifyAndReset();
+
+ // Make sure that the caller binder gets connected
+ EasyMock.verify(mockBinder);
+ EasyMock.reset(mockBinder);
+
+ // modify some number on wifi, and trigger poll event
+ // not enough traffic to call data usage callback
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
- // verify service recorded history:
- // 4000(dev) + 2000(dev)
- assertNetworkTotal(sTemplateImsi1, 6000L, 60L, 3000L, 30L, 0);
+ // verify service recorded history
verifyAndReset();
+ assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
- // create traffic on both DEV and XT, across two buckets
- incrementCurrentTime(2 * HOUR_IN_MILLIS);
+ // make sure callback has not being called
+ assertEquals(INVALID_TYPE, latchedHandler.mLastMessageType);
+
+ // and bump forward again, with counters going higher. this is
+ // important, since it will trigger the data usage callback
+ incrementCurrentTime(DAY_IN_MILLIS);
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 6004L, 64L, 3004L, 34L));
- expectNetworkStatsSummaryXt(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 10240L, 0L, 0L, 0L));
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ forcePollAndWaitForIdle();
- // verify that we switching reporting at the first atomic XT bucket,
- // which should give us:
- // 4000(dev) + 2000(dev) + 1(dev) + 5120(xt) + 2560(xt)
- assertNetworkTotal(sTemplateImsi1, 13681L, 61L, 3001L, 31L, 0);
+ // verify service recorded history
+ assertNetworkTotal(sTemplateWifi, 4096000L, 4L, 8192000L, 8L, 0);
+ verifyAndReset();
- // also test pure-DEV and pure-XT ranges
- assertNetworkTotal(sTemplateImsi1, startTimeMillis(),
- startTimeMillis() + 2 * HOUR_IN_MILLIS, 6001L, 61L, 3001L, 31L, 0);
- assertNetworkTotal(sTemplateImsi1, startTimeMillis() + 2 * HOUR_IN_MILLIS,
- startTimeMillis() + 4 * HOUR_IN_MILLIS, 7680L, 0L, 0L, 0L, 0);
+ // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
+ assertTrue(cv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.mLastMessageType);
+ cv.close();
+
+ // Allow binder to disconnect
+ expect(mockBinder.unlinkToDeath((IBinder.DeathRecipient) anyObject(), anyInt()))
+ .andReturn(true);
+ EasyMock.replay(mockBinder);
+
+ // Unregister request
+ mService.unregisterDataUsageRequest(request);
+
+ // Wait for the caller to ack receipt of CALLBACK_RELEASED
+ assertTrue(cv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.mLastMessageType);
+
+ // Make sure that the caller binder gets disconnected
+ EasyMock.verify(mockBinder);
+ }
+
+ public void testRegisterDataUsageCallback_uids() throws Exception {
+ // pretend that network comes online
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_1, true /* isRoaming */));
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
+
+ replay();
+ mService.forceUpdateIfaces();
+ verifyAndReset();
+
+ String callingPackage = "the.calling.package";
+ long thresholdInBytes = 10 * 1024 * 1024; // 10 MB
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1, sTemplateImsi2 };
+ int[] uids = new int[] { UID_RED };
+ DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, templates, uids, thresholdInBytes);
+
+ // Create a messenger that waits for callback activity
+ ConditionVariable cv = new ConditionVariable(false);
+ cv.close();
+ LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv);
+ Messenger messenger = new Messenger(latchedHandler);
+
+ // Allow binder to connect
+ IBinder mockBinder = createMock(IBinder.class);
+ mockBinder.linkToDeath((IBinder.DeathRecipient) anyObject(), anyInt());
+ EasyMock.replay(mockBinder);
+
+ // Force poll
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsPoll();
+ replay();
+
+ // Register and verify request and that binder was called
+ DataUsageRequest request =
+ mService.registerDataUsageCallback(callingPackage, inputRequest,
+ messenger, mockBinder);
+ assertTrue(request.requestId > 0);
+ assertTrue(Arrays.deepEquals(templates, request.templates));
+ assertTrue(Arrays.equals(uids, request.uids));
+ assertEquals(thresholdInBytes, request.thresholdInBytes);
+
+ // Wait for service to handle internal MSG_REGISTER_DATA_USAGE_LISTENER
+ mHandler.sendMessage(mHandler.obtainMessage(-1));
+ mHandlerThread.waitForIdle(WAIT_TIMEOUT);
verifyAndReset();
+
+ // Make sure that the caller binder gets connected
+ EasyMock.verify(mockBinder);
+ EasyMock.reset(mockBinder);
+
+ // modify some number on mobile interface, and trigger poll event
+ // not enough traffic to call data usage callback
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ 128L, 2L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ 1L, 0L));
+ expectNetworkStatsPoll();
+
+ replay();
+ forcePollAndWaitForIdle();
+
+ // verify service recorded history
+ assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0);
+
+ // verify entire history present
+ NetworkStats stats = mSession.getSummaryForAllUid(
+ sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(2, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 2L,
+ 128L, 2L, 0);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING, 64L, 1L, 64L,
+ 1L, 0);
+
+ verifyAndReset();
+
+ // make sure callback has not being called
+ assertEquals(INVALID_TYPE, latchedHandler.mLastMessageType);
+
+ // and bump forward again, with counters going higher. this is
+ // important, since it will trigger the data usage callback
+ incrementCurrentTime(DAY_IN_MILLIS);
+ expectCurrentTime();
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ 128000000L, 2L, 128000000L, 2L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT,
+ 64000000L, 1L, 64000000L, 1L, 0L));
+ expectNetworkStatsPoll();
+
+ replay();
+ forcePollAndWaitForIdle();
+
+ // verify service recorded history
+ assertUidTotal(sTemplateImsi1, UID_RED, 128000000L, 2L, 128000000L, 2L, 0);
+
+ // verify entire history present
+ stats = mSession.getSummaryForAllUid(
+ sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(2, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING,
+ 128000000L, 2L, 128000000L, 2L, 0);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING,
+ 64000000L, 1L, 64000000L, 1L, 0);
+
+ verifyAndReset();
+
+ // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
+ assertTrue(cv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.mLastMessageType);
+ cv.close();
+
+ // Allow binder to disconnect
+ expect(mockBinder.unlinkToDeath((IBinder.DeathRecipient) anyObject(), anyInt()))
+ .andReturn(true);
+ EasyMock.replay(mockBinder);
+
+ // Unregister request
+ mService.unregisterDataUsageRequest(request);
+
+ // Wait for the caller to ack receipt of CALLBACK_RELEASED
+ assertTrue(cv.block(WAIT_TIMEOUT));
+ assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.mLastMessageType);
+
+ // Make sure that the caller binder gets disconnected
+ EasyMock.verify(mockBinder);
+ }
+
+ public void testUnregisterDataUsageCallback_unknown_noop() throws Exception {
+ String callingPackage = "the.calling.package";
+ long thresholdInBytes = 10 * 1024 * 1024; // 10 MB
+ NetworkTemplate[] templates = new NetworkTemplate[] { sTemplateImsi1, sTemplateImsi2 };
+ DataUsageRequest unknownRequest = new DataUsageRequest(
+ 2, templates, null /* uids */, thresholdInBytes);
+
+ mService.unregisterDataUsageRequest(unknownRequest);
+ }
+
+ private static File getBaseDir(File statsDir) {
+ File baseDir = new File(statsDir, "netstats");
+ baseDir.mkdirs();
+ return baseDir;
}
private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
@@ -851,17 +1180,19 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
- assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes,
- txPackets, operations);
+ assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, rxBytes,
+ rxPackets, txBytes, txPackets, operations);
}
private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) throws Exception {
- assertUidTotal(template, uid, SET_ALL, rxBytes, rxPackets, txBytes, txPackets, operations);
+ assertUidTotal(template, uid, SET_ALL, ROAMING_ALL, rxBytes, rxPackets, txBytes, txPackets,
+ operations);
}
- private void assertUidTotal(NetworkTemplate template, int uid, int set, long rxBytes,
- long rxPackets, long txBytes, long txPackets, int operations) throws Exception {
+ private void assertUidTotal(NetworkTemplate template, int uid, int set, int roaming,
+ long rxBytes, long rxPackets, long txBytes, long txPackets, int operations)
+ throws Exception {
// verify history API
final NetworkStatsHistory history = mSession.getHistoryForUid(
template, uid, set, TAG_NONE, FIELD_ALL);
@@ -871,24 +1202,16 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForAllUid(
template, Long.MIN_VALUE, Long.MAX_VALUE, false);
- assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets,
- operations);
+ assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, roaming, rxBytes, rxPackets, txBytes,
+ txPackets, operations);
}
private void expectSystemReady() throws Exception {
- mAlarmManager.remove(isA(PendingIntent.class), null);
- expectLastCall().anyTimes();
-
- mAlarmManager.set(getContext().getPackageName(),
- eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
- anyInt(), isA(PendingIntent.class), null, null, isA(WorkSource.class),
- isA(AlarmManager.AlarmClockInfo.class));
- expectLastCall().atLeastOnce();
-
mNetManager.setGlobalAlert(anyLong());
expectLastCall().atLeastOnce();
- expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectBandwidthControlCheck();
}
private void expectNetworkState(NetworkState... state) throws Exception {
@@ -899,6 +1222,8 @@
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
+ expect(mConnManager.getAllVpnInfo()).andReturn(new VpnInfo[0]).atLeastOnce();
+
expectNetworkStatsSummaryDev(summary);
expectNetworkStatsSummaryXt(summary);
}
@@ -961,6 +1286,10 @@
expectLastCall().anyTimes();
}
+ private void expectBandwidthControlCheck() throws Exception {
+ expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+ }
+
private void assertStatsFilesExist(boolean exist) {
final File basePath = new File(mStatsDir, "netstats");
if (exist) {
@@ -971,18 +1300,31 @@
}
private static void assertValues(NetworkStats stats, String iface, int uid, int set,
- int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
+ int tag, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ int operations) {
final NetworkStats.Entry entry = new NetworkStats.Entry();
+ List<Integer> sets = new ArrayList<>();
if (set == SET_DEFAULT || set == SET_ALL) {
- final int i = stats.findIndex(iface, uid, SET_DEFAULT, tag);
- if (i != -1) {
- entry.add(stats.getValues(i, null));
- }
+ sets.add(SET_DEFAULT);
}
if (set == SET_FOREGROUND || set == SET_ALL) {
- final int i = stats.findIndex(iface, uid, SET_FOREGROUND, tag);
- if (i != -1) {
- entry.add(stats.getValues(i, null));
+ sets.add(SET_FOREGROUND);
+ }
+
+ List<Integer> roamings = new ArrayList<>();
+ if (roaming == ROAMING_DEFAULT || roaming == ROAMING_ALL) {
+ roamings.add(ROAMING_DEFAULT);
+ }
+ if (roaming == ROAMING_ROAMING || roaming == ROAMING_ALL) {
+ roamings.add(ROAMING_ROAMING);
+ }
+
+ for (int s : sets) {
+ for (int r : roamings) {
+ final int i = stats.findIndex(iface, uid, s, tag, r);
+ if (i != -1) {
+ entry.add(stats.getValues(i, null));
+ }
}
}
@@ -1012,9 +1354,14 @@
}
private static NetworkState buildMobile3gState(String subscriberId) {
+ return buildMobile3gState(subscriberId, false /* isRoaming */);
+ }
+
+ private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
final NetworkInfo info = new NetworkInfo(
TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
+ info.setRoaming(isRoaming);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
return new NetworkState(info, prop, null, null, subscriberId, null);
@@ -1049,11 +1396,75 @@
}
private void replay() {
- EasyMock.replay(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
+ EasyMock.replay(mNetManager, mTime, mSettings, mConnManager);
}
private void verifyAndReset() {
- EasyMock.verify(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
- EasyMock.reset(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
+ EasyMock.verify(mNetManager, mTime, mSettings, mConnManager);
+ EasyMock.reset(mNetManager, mTime, mSettings, mConnManager);
}
+
+ private void forcePollAndWaitForIdle() {
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ // Send dummy message to make sure that any previous message has been handled
+ mHandler.sendMessage(mHandler.obtainMessage(-1));
+ mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ }
+
+ static class LatchedHandler extends Handler {
+ private final ConditionVariable mCv;
+ int mLastMessageType = INVALID_TYPE;
+
+ LatchedHandler(Looper looper, ConditionVariable cv) {
+ super(looper);
+ mCv = cv;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ mLastMessageType = msg.what;
+ mCv.open();
+ super.handleMessage(msg);
+ }
+ }
+
+ /**
+ * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle
+ * will return immediately if the handler is already idle.
+ */
+ static class IdleableHandlerThread extends HandlerThread {
+ private IdleHandler mIdleHandler;
+
+ public IdleableHandlerThread(String name) {
+ super(name);
+ }
+
+ public void waitForIdle(long timeoutMs) {
+ final ConditionVariable cv = new ConditionVariable();
+ final MessageQueue queue = getLooper().getQueue();
+
+ synchronized (queue) {
+ if (queue.isIdle()) {
+ return;
+ }
+
+ assertNull("BUG: only one idle handler allowed", mIdleHandler);
+ mIdleHandler = new IdleHandler() {
+ public boolean queueIdle() {
+ cv.open();
+ mIdleHandler = null;
+ return false; // Remove the handler.
+ }
+ };
+ queue.addIdleHandler(mIdleHandler);
+ }
+
+ if (!cv.block(timeoutMs)) {
+ fail("HandlerThread " + getName() + " did not become idle after " + timeoutMs
+ + " ms");
+ queue.removeIdleHandler(mIdleHandler);
+ }
+ }
+ }
+
}