Merge "Use public resouce in android.R package"
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index d3d8bba..223bdcd 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -129,6 +129,11 @@
"src/android/net/EthernetNetworkSpecifier.java",
"src/android/net/IEthernetManager.aidl",
"src/android/net/IEthernetServiceListener.aidl",
+ "src/android/net/IInternalNetworkManagementListener.aidl",
+ "src/android/net/InternalNetworkUpdateRequest.java",
+ "src/android/net/InternalNetworkUpdateRequest.aidl",
+ "src/android/net/InternalNetworkManagementException.java",
+ "src/android/net/InternalNetworkManagementException.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
diff --git a/framework-t/src/android/app/usage/NetworkStatsManager.java b/framework-t/src/android/app/usage/NetworkStatsManager.java
index 683678a..28f930f 100644
--- a/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -21,6 +21,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -39,14 +40,11 @@
import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
+import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
-import android.os.Binder;
import android.os.Build;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -57,6 +55,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Provides access to network usage history and statistics. Usage data is collected in
@@ -157,6 +156,11 @@
setAugmentWithSubscriptionPlan(true);
}
+ /** @hide */
+ public INetworkStatsService getBinder() {
+ return mService;
+ }
+
/**
* Set poll on open flag to indicate the poll is needed before service gets statistics
* result. This is default enabled. However, for any non-privileged caller, the poll might
@@ -718,26 +722,36 @@
}
}
- /** @hide */
- public void registerUsageCallback(NetworkTemplate template, int networkType,
- long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
+ /**
+ * Registers to receive notifications about data usage on specified networks.
+ *
+ * <p>The callbacks will continue to be called as long as the process is alive or
+ * {@link #unregisterUsageCallback} is called.
+ *
+ * @param template Template used to match networks. See {@link NetworkTemplate}.
+ * @param thresholdBytes Threshold in bytes to be notified on. The provided value that lower
+ * than 2MiB will be clamped for non-privileged callers.
+ * @param executor The executor on which callback will be invoked. The provided {@link Executor}
+ * must run callback sequentially, otherwise the order of callbacks cannot be
+ * guaranteed.
+ * @param callback The {@link UsageCallback} that the system will call when data usage
+ * has exceeded the specified threshold.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public void registerUsageCallback(@NonNull NetworkTemplate template, long thresholdBytes,
+ @NonNull @CallbackExecutor Executor executor, @NonNull UsageCallback callback) {
+ Objects.requireNonNull(template, "NetworkTemplate cannot be null");
Objects.requireNonNull(callback, "UsageCallback cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
- final Looper looper;
- if (handler == null) {
- looper = Looper.myLooper();
- } else {
- looper = handler.getLooper();
- }
-
- DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
+ final DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
template, thresholdBytes);
try {
- CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
- template.getSubscriberId(), callback);
+ final UsageCallbackWrapper callbackWrapper =
+ new UsageCallbackWrapper(executor, callback);
callback.request = mService.registerUsageCallback(
- mContext.getOpPackageName(), request, new Messenger(callbackHandler),
- new Binder());
+ mContext.getOpPackageName(), request, callbackWrapper);
if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
if (callback.request == null) {
@@ -790,12 +804,15 @@
NetworkTemplate template = createTemplate(networkType, subscriberId);
if (DBG) {
Log.d(TAG, "registerUsageCallback called with: {"
- + " networkType=" + networkType
- + " subscriberId=" + subscriberId
- + " thresholdBytes=" + thresholdBytes
- + " }");
+ + " networkType=" + networkType
+ + " subscriberId=" + subscriberId
+ + " thresholdBytes=" + thresholdBytes
+ + " }");
}
- registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
+
+ final Executor executor = handler == null ? r -> r.run() : r -> handler.post(r);
+
+ registerUsageCallback(template, thresholdBytes, executor, callback);
}
/**
@@ -820,6 +837,26 @@
* Base class for usage callbacks. Should be extended by applications wanting notifications.
*/
public static abstract class UsageCallback {
+ /**
+ * Called when data usage has reached the given threshold.
+ *
+ * Called by {@code NetworkStatsService} when the registered threshold is reached.
+ * If a caller implements {@link #onThresholdReached(NetworkTemplate)}, the system
+ * will not call {@link #onThresholdReached(int, String)}.
+ *
+ * @param template The {@link NetworkTemplate} that associated with this callback.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public void onThresholdReached(@NonNull NetworkTemplate template) {
+ // Backward compatibility for those who didn't override this function.
+ final int networkType = networkTypeForTemplate(template);
+ if (networkType != ConnectivityManager.TYPE_NONE) {
+ final String subscriberId = template.getSubscriberIds().isEmpty() ? null
+ : template.getSubscriberIds().iterator().next();
+ onThresholdReached(networkType, subscriberId);
+ }
+ }
/**
* Called when data usage has reached the given threshold.
@@ -830,6 +867,25 @@
* @hide used for internal bookkeeping
*/
private DataUsageRequest request;
+
+ /**
+ * Get network type from a template if feasible.
+ *
+ * @param template the target {@link NetworkTemplate}.
+ * @return legacy network type, only supports for the types which is already supported in
+ * {@link #registerUsageCallback(int, String, long, UsageCallback, Handler)}.
+ * {@link ConnectivityManager#TYPE_NONE} for other types.
+ */
+ private static int networkTypeForTemplate(@NonNull NetworkTemplate template) {
+ switch (template.getMatchRule()) {
+ case NetworkTemplate.MATCH_MOBILE:
+ return ConnectivityManager.TYPE_MOBILE;
+ case NetworkTemplate.MATCH_WIFI:
+ return ConnectivityManager.TYPE_WIFI;
+ default:
+ return ConnectivityManager.TYPE_NONE;
+ }
+ }
}
/**
@@ -948,43 +1004,32 @@
}
}
- private static class CallbackHandler extends Handler {
- private final int mNetworkType;
- private final String mSubscriberId;
- private UsageCallback mCallback;
+ private static class UsageCallbackWrapper extends IUsageCallback.Stub {
+ // Null if unregistered.
+ private volatile UsageCallback mCallback;
- CallbackHandler(Looper looper, int networkType, String subscriberId,
- UsageCallback callback) {
- super(looper);
- mNetworkType = networkType;
- mSubscriberId = subscriberId;
+ private final Executor mExecutor;
+
+ UsageCallbackWrapper(@NonNull Executor executor, @NonNull UsageCallback callback) {
mCallback = callback;
+ mExecutor = executor;
}
@Override
- public void handleMessage(Message message) {
- DataUsageRequest request =
- (DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY);
-
- switch (message.what) {
- case CALLBACK_LIMIT_REACHED: {
- if (mCallback != null) {
- mCallback.onThresholdReached(mNetworkType, mSubscriberId);
- } else {
- Log.e(TAG, "limit reached with released callback for " + request);
- }
- break;
- }
- case CALLBACK_RELEASED: {
- if (DBG) Log.d(TAG, "callback released for " + request);
- mCallback = null;
- break;
- }
+ public void onThresholdReached(DataUsageRequest request) {
+ // Copy it to a local variable in case mCallback changed inside the if condition.
+ final UsageCallback callback = mCallback;
+ if (callback != null) {
+ mExecutor.execute(() -> callback.onThresholdReached(request.template));
+ } else {
+ Log.e(TAG, "onThresholdReached with released callback for " + request);
}
}
- private static Object getObject(Message msg, String key) {
- return msg.getData().getParcelable(key);
+ @Override
+ public void onCallbackReleased(DataUsageRequest request) {
+ if (DBG) Log.d(TAG, "callback released for " + request);
+ mCallback = null;
}
}
diff --git a/framework-t/src/android/net/INetworkStatsService.aidl b/framework-t/src/android/net/INetworkStatsService.aidl
index da0aa99..efe626d 100644
--- a/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/framework-t/src/android/net/INetworkStatsService.aidl
@@ -24,6 +24,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
+import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
@@ -71,7 +72,7 @@
/** Registers a callback on data usage. */
DataUsageRequest registerUsageCallback(String callingPackage,
- in DataUsageRequest request, in Messenger messenger, in IBinder binder);
+ in DataUsageRequest request, in IUsageCallback callback);
/** Unregisters a callback on data usage. */
void unregisterUsageRequest(in DataUsageRequest request);
diff --git a/core/java/android/net/InternalNetworkManagementException.aidl b/framework-t/src/android/net/InternalNetworkManagementException.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.aidl
rename to framework-t/src/android/net/InternalNetworkManagementException.aidl
diff --git a/core/java/android/net/InternalNetworkManagementException.java b/framework-t/src/android/net/InternalNetworkManagementException.java
similarity index 100%
rename from core/java/android/net/InternalNetworkManagementException.java
rename to framework-t/src/android/net/InternalNetworkManagementException.java
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.aidl b/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
similarity index 100%
rename from core/java/android/net/InternalNetworkUpdateRequest.aidl
rename to framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/framework-t/src/android/net/InternalNetworkUpdateRequest.java
similarity index 100%
rename from core/java/android/net/InternalNetworkUpdateRequest.java
rename to framework-t/src/android/net/InternalNetworkUpdateRequest.java
diff --git a/framework-t/src/android/net/NetworkIdentity.java b/framework-t/src/android/net/NetworkIdentity.java
index 9b9d38a..77fc171 100644
--- a/framework-t/src/android/net/NetworkIdentity.java
+++ b/framework-t/src/android/net/NetworkIdentity.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
@@ -23,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.service.NetworkIdentityProto;
@@ -30,6 +33,7 @@
import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
@@ -44,8 +48,8 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
-public class NetworkIdentity implements Comparable<NetworkIdentity> {
+@SystemApi(client = MODULE_LIBRARIES)
+public class NetworkIdentity {
private static final String TAG = "NetworkIdentity";
/** @hide */
@@ -55,7 +59,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "OEM_MANAGED_" }, value = {
+ @IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = {
NetworkTemplate.OEM_MANAGED_NO,
NetworkTemplate.OEM_MANAGED_PAID,
NetworkTemplate.OEM_MANAGED_PRIVATE
@@ -71,12 +75,14 @@
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
* @hide
*/
- public static final int OEM_PAID = 0x1;
+ public static final int OEM_PAID = 1 << 0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
* @hide
*/
- public static final int OEM_PRIVATE = 0x2;
+ public static final int OEM_PRIVATE = 1 << 1;
+
+ private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
final int mType;
final int mRatType;
@@ -206,7 +212,7 @@
return mSubscriberId;
}
- /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getCurrentNetworkKey()}. */
+ /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */
@Nullable
public String getWifiNetworkKey() {
return mWifiNetworkKey;
@@ -218,7 +224,7 @@
return mRoaming;
}
- /** Return the roaming status of this instance. */
+ /** Return whether this network is roaming. */
public boolean isRoaming() {
return mRoaming;
}
@@ -229,7 +235,7 @@
return mMetered;
}
- /** Return the meteredness of this instance. */
+ /** Return whether this network is metered. */
public boolean isMetered() {
return mMetered;
}
@@ -240,7 +246,7 @@
return mDefaultNetwork;
}
- /** Return the default network status of this instance. */
+ /** Return whether this network is the default network. */
public boolean isDefaultNetwork() {
return mDefaultNetwork;
}
@@ -262,7 +268,7 @@
* {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable.
* See {@code TelephonyManager.NETWORK_TYPE_*}.
* @hide
- * @deprecated See {@link NetworkIdentity#Builder}.
+ * @deprecated See {@link NetworkIdentity.Builder}.
*/
// TODO: Remove this after all callers are migrated to use new Api.
@Deprecated
@@ -270,8 +276,12 @@
public static NetworkIdentity buildNetworkIdentity(Context context,
@NonNull NetworkStateSnapshot snapshot,
boolean defaultNetwork, @Annotation.NetworkType int ratType) {
- return new NetworkIdentity.Builder().setNetworkStateSnapshot(snapshot)
- .setDefaultNetwork(defaultNetwork).setRatType(ratType).build();
+ final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+ if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
+ builder.setRatType(ratType);
+ }
+ return builder.build();
}
/**
@@ -291,30 +301,30 @@
return oemManaged;
}
- @Override
- public int compareTo(@NonNull NetworkIdentity another) {
- Objects.requireNonNull(another);
- int res = Integer.compare(mType, another.mType);
+ /** @hide */
+ public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
+ Objects.requireNonNull(right);
+ int res = Integer.compare(left.mType, right.mType);
if (res == 0) {
- res = Integer.compare(mRatType, another.mRatType);
+ res = Integer.compare(left.mRatType, right.mRatType);
}
- if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
- res = mSubscriberId.compareTo(another.mSubscriberId);
+ if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) {
+ res = left.mSubscriberId.compareTo(right.mSubscriberId);
}
- if (res == 0 && mWifiNetworkKey != null && another.mWifiNetworkKey != null) {
- res = mWifiNetworkKey.compareTo(another.mWifiNetworkKey);
+ if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) {
+ res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey);
}
if (res == 0) {
- res = Boolean.compare(mRoaming, another.mRoaming);
+ res = Boolean.compare(left.mRoaming, right.mRoaming);
}
if (res == 0) {
- res = Boolean.compare(mMetered, another.mMetered);
+ res = Boolean.compare(left.mMetered, right.mMetered);
}
if (res == 0) {
- res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+ res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork);
}
if (res == 0) {
- res = Integer.compare(mOemManaged, another.mOemManaged);
+ res = Integer.compare(left.mOemManaged, right.mOemManaged);
}
return res;
}
@@ -323,6 +333,11 @@
* Builder class for {@link NetworkIdentity}.
*/
public static final class Builder {
+ // Need to be synchronized with ConnectivityManager.
+ // TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module.
+ private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
private int mType;
private int mRatType;
private String mSubscriberId;
@@ -349,7 +364,14 @@
/**
* Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance.
- * This is to read roaming, metered, wifikey... from the snapshot for convenience.
+ * This is a useful shorthand that will read from the snapshot and set the
+ * following fields, if they are set in the snapshot :
+ * - type
+ * - subscriberId
+ * - roaming
+ * - metered
+ * - oemManaged
+ * - wifiNetworkKey
*
* @param snapshot The target {@link NetworkStateSnapshot} object.
* @return The builder object.
@@ -374,9 +396,7 @@
.getTransportInfo();
if (transportInfo instanceof WifiInfo) {
final WifiInfo info = (WifiInfo) transportInfo;
- if (info != null) {
- setWifiNetworkKey(info.getCurrentNetworkKey());
- }
+ setWifiNetworkKey(info.getNetworkKey());
}
}
return this;
@@ -391,6 +411,12 @@
*/
@NonNull
public Builder setType(int type) {
+ // Include TYPE_NONE for compatibility, type field might not be filled by some
+ // networks such as test networks.
+ if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type)
+ && type != ConnectivityManager.TYPE_NONE) {
+ throw new IllegalArgumentException("Invalid network type: " + type);
+ }
mType = type;
return this;
}
@@ -398,6 +424,8 @@
/**
* Set the Radio Access Technology(RAT) type of the network.
*
+ * No RAT type is specified by default. Call clearRatType to reset.
+ *
* @param ratType the Radio Access Technology(RAT) type if applicable. See
* {@code TelephonyManager.NETWORK_TYPE_*}.
*
@@ -405,6 +433,11 @@
*/
@NonNull
public Builder setRatType(@Annotation.NetworkType int ratType) {
+ if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
+ && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN
+ && ratType != NetworkTemplate.NETWORK_TYPE_5G_NSA) {
+ throw new IllegalArgumentException("Invalid ratType " + ratType);
+ }
mRatType = ratType;
return this;
}
@@ -436,7 +469,7 @@
* Set the Wifi Network Key.
*
* @param wifiNetworkKey Wifi Network Key of the network,
- * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * see {@link WifiInfo#getNetworkKey()}.
* Or null if not applicable.
* @return this builder.
*/
@@ -447,7 +480,9 @@
}
/**
- * Set the roaming.
+ * Set whether this network is roaming.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param roaming the roaming status of the network.
* @return this builder.
@@ -459,7 +494,9 @@
}
/**
- * Set the meteredness.
+ * Set whether this network is metered.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param metered the meteredness of the network.
* @return this builder.
@@ -471,7 +508,9 @@
}
/**
- * Set the default network status.
+ * Set whether this network is the default network.
+ *
+ * This field is false by default. Call with false to reset.
*
* @param defaultNetwork the default network status of the network.
* @return this builder.
@@ -491,10 +530,27 @@
*/
@NonNull
public Builder setOemManaged(@OemManaged int oemManaged) {
+ // Assert input does not contain illegal oemManage bits.
+ if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) {
+ throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged);
+ }
mOemManaged = oemManaged;
return this;
}
+ private void ensureValidParameters() {
+ // Assert non-mobile network cannot have a ratType.
+ if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
+ throw new IllegalArgumentException(
+ "Invalid ratType " + mRatType + " for type " + mType);
+ }
+
+ // Assert non-wifi network cannot have a wifi network key.
+ if (mType != TYPE_WIFI && mWifiNetworkKey != null) {
+ throw new IllegalArgumentException("Invalid wifi network key for type " + mType);
+ }
+ }
+
/**
* Builds the instance of the {@link NetworkIdentity}.
*
@@ -502,6 +558,7 @@
*/
@NonNull
public NetworkIdentity build() {
+ ensureValidParameters();
return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
mRoaming, mMetered, mDefaultNetwork, mOemManaged);
}
diff --git a/framework-t/src/android/net/NetworkIdentitySet.java b/framework-t/src/android/net/NetworkIdentitySet.java
index 041f070..dfa347f 100644
--- a/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/framework-t/src/android/net/NetworkIdentitySet.java
@@ -27,6 +27,7 @@
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
* Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
@@ -34,9 +35,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
-public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
- Comparable<NetworkIdentitySet> {
+public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_ROAMING = 2;
private static final int VERSION_ADD_NETWORK_ID = 3;
@@ -52,6 +51,11 @@
}
/** @hide */
+ public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) {
+ super(ident);
+ }
+
+ /** @hide */
public NetworkIdentitySet(DataInput in) throws IOException {
final int version = in.readInt();
final int size = in.readInt();
@@ -189,15 +193,15 @@
}
}
- @Override
- public int compareTo(@NonNull NetworkIdentitySet another) {
- Objects.requireNonNull(another);
- if (isEmpty()) return -1;
- if (another.isEmpty()) return 1;
+ public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
+ if (left.isEmpty()) return -1;
+ if (right.isEmpty()) return 1;
- final NetworkIdentity ident = iterator().next();
- final NetworkIdentity anotherIdent = another.iterator().next();
- return ident.compareTo(anotherIdent);
+ final NetworkIdentity leftIdent = left.iterator().next();
+ final NetworkIdentity rightIdent = right.iterator().next();
+ return NetworkIdentity.compare(leftIdent, rightIdent);
}
/**
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index f169fed..735c44d 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.IFACE_ALL;
@@ -34,6 +35,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStatsHistory.Entry;
import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto;
import android.service.NetworkStatsCollectionProto;
@@ -71,7 +74,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
@@ -79,7 +85,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
private static final String TAG = NetworkStatsCollection.class.getSimpleName();
/** File header magic number: "ANET" */
@@ -702,7 +708,7 @@
private ArrayList<Key> getSortedKeys() {
final ArrayList<Key> keys = new ArrayList<>();
keys.addAll(mStats.keySet());
- Collections.sort(keys);
+ Collections.sort(keys, (left, right) -> Key.compare(left, right));
return keys;
}
@@ -809,10 +815,75 @@
}
/**
+ * Get the all historical stats of the collection {@link NetworkStatsCollection}.
+ *
+ * @return All {@link NetworkStatsHistory} in this collection.
+ */
+ @NonNull
+ public Map<Key, NetworkStatsHistory> getEntries() {
+ return new ArrayMap(mStats);
+ }
+
+ /**
+ * Builder class for {@link NetworkStatsCollection}.
+ */
+ public static final class Builder {
+ private final long mBucketDuration;
+ private final ArrayMap<Key, NetworkStatsHistory> mEntries = new ArrayMap<>();
+
+ /**
+ * Creates a new Builder with given bucket duration.
+ *
+ * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+ */
+ public Builder(long bucketDuration) {
+ mBucketDuration = bucketDuration;
+ }
+
+ /**
+ * Add association of the history with the specified key in this map.
+ *
+ * @param key The object used to identify a network, see {@link Key}.
+ * @param history {@link NetworkStatsHistory} instance associated to the given {@link Key}.
+ * @return The builder object.
+ */
+ @NonNull
+ public NetworkStatsCollection.Builder addEntry(@NonNull Key key,
+ @NonNull NetworkStatsHistory history) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(history);
+ final List<Entry> historyEntries = history.getEntries();
+
+ final NetworkStatsHistory.Builder historyBuilder =
+ new NetworkStatsHistory.Builder(mBucketDuration, historyEntries.size());
+ for (Entry entry : historyEntries) {
+ historyBuilder.addEntry(entry);
+ }
+
+ mEntries.put(key, historyBuilder.build());
+ return this;
+ }
+
+ /**
+ * Builds the instance of the {@link NetworkStatsCollection}.
+ *
+ * @return the built instance of {@link NetworkStatsCollection}.
+ */
+ @NonNull
+ public NetworkStatsCollection build() {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+ for (int i = 0; i < mEntries.size(); i++) {
+ collection.recordHistory(mEntries.keyAt(i), mEntries.valueAt(i));
+ }
+ return collection;
+ }
+ }
+
+ /**
* the identifier that associate with the {@link NetworkStatsHistory} object to identify
* a certain record in the {@link NetworkStatsCollection} object.
*/
- public static class Key implements Comparable<Key> {
+ public static class Key {
/** @hide */
public final NetworkIdentitySet ident;
/** @hide */
@@ -832,6 +903,11 @@
* @param set Set of the record, see {@code NetworkStats#SET_*}.
* @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}.
*/
+ public Key(@NonNull Set<NetworkIdentity> ident, int uid, int set, int tag) {
+ this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag);
+ }
+
+ /** @hide */
public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) {
this.ident = Objects.requireNonNull(ident);
this.uid = uid;
@@ -855,21 +931,22 @@
return false;
}
- @Override
- public int compareTo(@NonNull Key another) {
- Objects.requireNonNull(another);
+ /** @hide */
+ public static int compare(@NonNull Key left, @NonNull Key right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
int res = 0;
- if (ident != null && another.ident != null) {
- res = ident.compareTo(another.ident);
+ if (left.ident != null && right.ident != null) {
+ res = NetworkIdentitySet.compare(left.ident, right.ident);
}
if (res == 0) {
- res = Integer.compare(uid, another.uid);
+ res = Integer.compare(left.uid, right.uid);
}
if (res == 0) {
- res = Integer.compare(set, another.set);
+ res = Integer.compare(left.set, right.set);
}
if (res == 0) {
- res = Integer.compare(tag, another.tag);
+ res = Integer.compare(left.tag, right.tag);
}
return res;
}
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 90054c6..78c1370 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
@@ -31,6 +32,7 @@
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -51,7 +53,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
/**
@@ -65,7 +69,7 @@
*
* @hide
*/
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_PACKETS = 2;
@@ -97,23 +101,157 @@
private long[] operations;
private long totalBytes;
- public static class Entry {
+ /** @hide */
+ public NetworkStatsHistory(long bucketDuration, long[] bucketStart, long[] activeTime,
+ long[] rxBytes, long[] rxPackets, long[] txBytes, long[] txPackets,
+ long[] operations, int bucketCount, long totalBytes) {
+ this.bucketDuration = bucketDuration;
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ this.bucketCount = bucketCount;
+ this.totalBytes = totalBytes;
+ }
+
+ /**
+ * An instance to represent a single record in a {@link NetworkStatsHistory} object.
+ */
+ public static final class Entry {
+ /** @hide */
public static final long UNKNOWN = -1;
+ /** @hide */
+ // TODO: Migrate all callers to get duration from the history object and remove this field.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketDuration;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketStart;
+ /** @hide */
public long activeTime;
+ /** @hide */
@UnsupportedAppUsage
public long rxBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long rxPackets;
+ /** @hide */
@UnsupportedAppUsage
public long txBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long txPackets;
+ /** @hide */
public long operations;
+ /** @hide */
+ Entry() {}
+
+ /**
+ * Construct a {@link Entry} instance to represent a single record in a
+ * {@link NetworkStatsHistory} object.
+ *
+ * @param bucketStart Start of period for this {@link Entry}, in milliseconds since the
+ * Unix epoch, see {@link java.lang.System#currentTimeMillis}.
+ * @param activeTime Active time for this {@link Entry}, in milliseconds.
+ * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param operations count of network operations performed for this {@link Entry}. This can
+ * be used to derive bytes-per-operation.
+ */
+ public Entry(long bucketStart, long activeTime, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ }
+
+ /**
+ * Get start timestamp of the bucket's time interval, in milliseconds since the Unix epoch.
+ */
+ public long getBucketStart() {
+ return bucketStart;
+ }
+
+ /**
+ * Get active time of the bucket's time interval, in milliseconds.
+ */
+ public long getActiveTime() {
+ return activeTime;
+ }
+
+ /** Get number of bytes received for this {@link Entry}. */
+ public long getRxBytes() {
+ return rxBytes;
+ }
+
+ /** Get number of packets received for this {@link Entry}. */
+ public long getRxPackets() {
+ return rxPackets;
+ }
+
+ /** Get number of bytes transmitted for this {@link Entry}. */
+ public long getTxBytes() {
+ return txBytes;
+ }
+
+ /** Get number of packets transmitted for this {@link Entry}. */
+ public long getTxPackets() {
+ return txPackets;
+ }
+
+ /** Get count of network operations performed for this {@link Entry}. */
+ public long getOperations() {
+ return operations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o.getClass() != getClass()) return false;
+ Entry entry = (Entry) o;
+ return bucketStart == entry.bucketStart
+ && activeTime == entry.activeTime && rxBytes == entry.rxBytes
+ && rxPackets == entry.rxPackets && txBytes == entry.txBytes
+ && txPackets == entry.txPackets && operations == entry.operations;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (bucketStart * 2
+ + activeTime * 3
+ + rxBytes * 5
+ + rxPackets * 7
+ + txBytes * 11
+ + txPackets * 13
+ + operations * 17);
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{"
+ + "bucketStart=" + bucketStart
+ + ", activeTime=" + activeTime
+ + ", rxBytes=" + rxBytes
+ + ", rxPackets=" + rxPackets
+ + ", txBytes=" + txBytes
+ + ", txPackets=" + txPackets
+ + ", operations=" + operations
+ + "}";
+ }
}
/** @hide */
@@ -324,6 +462,22 @@
return entry;
}
+ /**
+ * Get List of {@link Entry} of the {@link NetworkStatsHistory} instance.
+ *
+ * @return
+ */
+ @NonNull
+ public List<Entry> getEntries() {
+ // TODO: Return a wrapper that uses this list instead, to prevent the returned result
+ // from being changed.
+ final ArrayList<Entry> ret = new ArrayList<>(size());
+ for (int i = 0; i < size(); i++) {
+ ret.add(getValues(i, null /* recycle */));
+ }
+ return ret;
+ }
+
/** @hide */
public void setValues(int i, Entry entry) {
// Unwind old values
@@ -928,4 +1082,80 @@
}
}
+ /**
+ * Builder class for {@link NetworkStatsHistory}.
+ */
+ public static final class Builder {
+ private final long mBucketDuration;
+ private final List<Long> mBucketStart;
+ private final List<Long> mActiveTime;
+ private final List<Long> mRxBytes;
+ private final List<Long> mRxPackets;
+ private final List<Long> mTxBytes;
+ private final List<Long> mTxPackets;
+ private final List<Long> mOperations;
+
+ /**
+ * Creates a new Builder with given bucket duration and initial capacity to construct
+ * {@link NetworkStatsHistory} objects.
+ *
+ * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+ * @param initialCapacity Estimated number of records.
+ */
+ public Builder(long bucketDuration, int initialCapacity) {
+ mBucketDuration = bucketDuration;
+ mBucketStart = new ArrayList<>(initialCapacity);
+ mActiveTime = new ArrayList<>(initialCapacity);
+ mRxBytes = new ArrayList<>(initialCapacity);
+ mRxPackets = new ArrayList<>(initialCapacity);
+ mTxBytes = new ArrayList<>(initialCapacity);
+ mTxPackets = new ArrayList<>(initialCapacity);
+ mOperations = new ArrayList<>(initialCapacity);
+ }
+
+ /**
+ * Add an {@link Entry} into the {@link NetworkStatsHistory} instance.
+ *
+ * @param entry The target {@link Entry} object.
+ * @return The builder object.
+ */
+ @NonNull
+ public Builder addEntry(@NonNull Entry entry) {
+ mBucketStart.add(entry.bucketStart);
+ mActiveTime.add(entry.activeTime);
+ mRxBytes.add(entry.rxBytes);
+ mRxPackets.add(entry.rxPackets);
+ mTxBytes.add(entry.txBytes);
+ mTxPackets.add(entry.txPackets);
+ mOperations.add(entry.operations);
+ return this;
+ }
+
+ private static long sum(@NonNull List<Long> list) {
+ long sum = 0;
+ for (long entry : list) {
+ sum += entry;
+ }
+ return sum;
+ }
+
+ /**
+ * Builds the instance of the {@link NetworkStatsHistory}.
+ *
+ * @return the built instance of {@link NetworkStatsHistory}.
+ */
+ @NonNull
+ public NetworkStatsHistory build() {
+ return new NetworkStatsHistory(mBucketDuration,
+ CollectionUtils.toLongArray(mBucketStart),
+ CollectionUtils.toLongArray(mActiveTime),
+ CollectionUtils.toLongArray(mRxBytes),
+ CollectionUtils.toLongArray(mRxPackets),
+ CollectionUtils.toLongArray(mTxBytes),
+ CollectionUtils.toLongArray(mTxPackets),
+ CollectionUtils.toLongArray(mOperations),
+ mBucketStart.size(),
+ sum(mRxBytes) + sum(mTxBytes));
+ }
+ }
}
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index a7e48d4..dba3991 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -263,7 +263,7 @@
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @hide
*/
@@ -283,7 +283,7 @@
* Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
* of key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @param subscriberId the IMSI associated to this wifi network.
*
@@ -593,7 +593,7 @@
/**
* Get the set of Wifi Network Keys of the template.
- * See {@link WifiInfo#getCurrentNetworkKey()}.
+ * See {@link WifiInfo#getNetworkKey()}.
*/
@NonNull
public Set<String> getWifiNetworkKeys() {
@@ -652,7 +652,9 @@
*
* @hide
*/
- public boolean matches(NetworkIdentity ident) {
+ @SystemApi(client = MODULE_LIBRARIES)
+ public boolean matches(@NonNull NetworkIdentity ident) {
+ Objects.requireNonNull(ident);
if (!matchesMetered(ident)) return false;
if (!matchesRoaming(ident)) return false;
if (!matchesDefaultNetwork(ident)) return false;
@@ -729,7 +731,7 @@
* Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
* empty.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
*/
private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
@@ -1059,9 +1061,9 @@
* the intention of matching any Wifi Network Key.
*
* @param wifiNetworkKeys the list of Wifi Network Key,
- * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * see {@link WifiInfo#getNetworkKey()}.
* Or an empty list to match all networks.
- * Note that {@code getCurrentNetworkKey()} might get null key
+ * Note that {@code getNetworkKey()} might get null key
* when wifi disconnects. However, the caller should never invoke
* this function with a null Wifi Network Key since such statistics
* never exists.
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index 1af32bf..c2f0cdf 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -16,8 +16,9 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -27,9 +28,10 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.MediaPlayer;
+import android.os.Binder;
import android.os.Build;
-import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import com.android.server.NetworkManagementSocketTagger;
@@ -37,8 +39,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
@@ -177,25 +177,12 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private synchronized static INetworkStatsService getStatsService() {
if (sStatsService == null) {
- sStatsService = getStatsBinder();
+ throw new IllegalStateException("TrafficStats not initialized, uid="
+ + Binder.getCallingUid());
}
return sStatsService;
}
- @Nullable
- private static INetworkStatsService getStatsBinder() {
- try {
- final Method getServiceMethod = Class.forName("android.os.ServiceManager")
- .getDeclaredMethod("getService", new Class[]{String.class});
- final IBinder binder = (IBinder) getServiceMethod.invoke(
- null, Context.NETWORK_STATS_SERVICE);
- return INetworkStatsService.Stub.asInterface(binder);
- } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
- | InvocationTargetException e) {
- throw new NullPointerException("Cannot get INetworkStatsService: " + e);
- }
- }
-
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
@@ -210,6 +197,45 @@
private static final String LOOPBACK_IFACE = "lo";
/**
+ * Initialization {@link TrafficStats} with the context, to
+ * allow {@link TrafficStats} to fetch the needed binder.
+ *
+ * @param context a long-lived context, such as the application context or system
+ * server context.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("VisiblySynchronized")
+ public static synchronized void init(@NonNull final Context context) {
+ if (sStatsService != null) {
+ throw new IllegalStateException("TrafficStats is already initialized, uid="
+ + Binder.getCallingUid());
+ }
+ final NetworkStatsManager statsManager =
+ context.getSystemService(NetworkStatsManager.class);
+ if (statsManager == null) {
+ // TODO: Currently Process.isSupplemental is not working yet, because it depends on
+ // process to run in a certain UID range, which is not true for now. Change this
+ // to Log.wtf once Process.isSupplemental is ready.
+ Log.e(TAG, "TrafficStats not initialized, uid=" + Binder.getCallingUid());
+ return;
+ }
+ sStatsService = statsManager.getBinder();
+ }
+
+ /**
+ * Attach the socket tagger implementation to the current process, to
+ * get notified when a socket's {@link FileDescriptor} is assigned to
+ * a thread. See {@link SocketTagger#set(SocketTagger)}.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void attachSocketTagger() {
+ NetworkManagementSocketTagger.install();
+ }
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
diff --git a/framework-t/src/android/net/netstats/IUsageCallback.aidl b/framework-t/src/android/net/netstats/IUsageCallback.aidl
new file mode 100644
index 0000000..4e8a5b2
--- /dev/null
+++ b/framework-t/src/android/net/netstats/IUsageCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netstats;
+
+import android.net.DataUsageRequest;
+
+/**
+ * Interface for NetworkStatsService to notify events to the callers of registerUsageCallback.
+ *
+ * @hide
+ */
+oneway interface IUsageCallback {
+ void onThresholdReached(in DataUsageRequest request);
+ void onCallbackReleased(in DataUsageRequest request);
+}
diff --git a/service-t/Sources.bp b/service-t/Sources.bp
index b261e16..24bc91d 100644
--- a/service-t/Sources.bp
+++ b/service-t/Sources.bp
@@ -26,6 +26,8 @@
srcs: [
"src/com/android/server/net/NetworkIdentity*.java",
"src/com/android/server/net/NetworkStats*.java",
+ "src/com/android/server/net/BpfInterfaceMapUpdater.java",
+ "src/com/android/server/net/InterfaceMapValue.java",
],
path: "src",
visibility: [
@@ -66,6 +68,7 @@
filegroup {
name: "services.connectivity-ethernet-sources",
srcs: [
+ "src/com/android/server/net/DelayedDiskWrite.java",
"src/com/android/server/net/IpConfigStore.java",
],
path: "src",
@@ -97,3 +100,28 @@
"//packages/modules/Connectivity:__subpackages__",
],
}
+
+cc_library_shared {
+ name: "libcom_android_net_module_util_jni",
+ min_sdk_version: "30",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "jni/onload.cpp",
+ ],
+ stl: "libc++_static",
+ static_libs: [
+ "libnet_utils_device_common_bpfjni",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnativehelper",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/service-t/jni/onload.cpp b/service-t/jni/onload.cpp
new file mode 100644
index 0000000..bca4697
--- /dev/null
+++ b/service-t/jni/onload.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
+
+namespace android {
+
+int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_com_android_net_module_util_BpfMap(env,
+ "com/android/net/module/util/BpfMap") < 0) return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+};
+
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
new file mode 100644
index 0000000..25c88eb
--- /dev/null
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 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 android.content.Context;
+import android.net.INetd;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.Struct.U32;
+
+/**
+ * Monitor interface added (without removed) and right interface name and its index to bpf map.
+ */
+public class BpfInterfaceMapUpdater {
+ private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
+ // This is current path but may be changed soon.
+ private static final String IFACE_INDEX_NAME_MAP_PATH =
+ "/sys/fs/bpf/map_netd_iface_index_name_map";
+ private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
+ private final INetd mNetd;
+ private final Handler mHandler;
+ private final Dependencies mDeps;
+
+ public BpfInterfaceMapUpdater(Context ctx, Handler handler) {
+ this(ctx, handler, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
+ mDeps = deps;
+ mBpfMap = deps.getInterfaceMap();
+ mNetd = deps.getINetd(ctx);
+ mHandler = handler;
+ }
+
+ /**
+ * Dependencies of BpfInerfaceMapUpdater, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /** Create BpfMap for updating interface and index mapping. */
+ public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
+ try {
+ return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
+ U32.class, InterfaceMapValue.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create interface map: " + e);
+ return null;
+ }
+ }
+
+ /** Get InterfaceParams for giving interface name. */
+ public InterfaceParams getInterfaceParams(String ifaceName) {
+ return InterfaceParams.getByName(ifaceName);
+ }
+
+ /** Get INetd binder object. */
+ public INetd getINetd(Context ctx) {
+ return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE));
+ }
+ }
+
+ /**
+ * Start listening interface update event.
+ * Query current interface names before listening.
+ */
+ public void start() {
+ mHandler.post(() -> {
+ if (mBpfMap == null) {
+ Log.wtf(TAG, "Fail to start: Null bpf map");
+ return;
+ }
+
+ try {
+ // TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead.
+ mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver());
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e);
+ }
+
+ final String[] ifaces;
+ try {
+ // TODO: use a netlink dump to get the current interface list.
+ ifaces = mNetd.interfaceGetList();
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.wtf(TAG, "Unable to query interface names by netd, " + e);
+ return;
+ }
+
+ for (String ifaceName : ifaces) {
+ addInterface(ifaceName);
+ }
+ });
+ }
+
+ private void addInterface(String ifaceName) {
+ final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName);
+ if (iface == null) {
+ Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName);
+ return;
+ }
+
+ try {
+ mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName));
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
+ }
+ }
+
+ private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onInterfaceAdded(String ifName) {
+ mHandler.post(() -> addInterface(ifName));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/service-t/src/com/android/server/net/DelayedDiskWrite.java
similarity index 82%
rename from services/core/java/com/android/server/net/DelayedDiskWrite.java
rename to service-t/src/com/android/server/net/DelayedDiskWrite.java
index 8f09eb7..35dc455 100644
--- a/services/core/java/com/android/server/net/DelayedDiskWrite.java
+++ b/service-t/src/com/android/server/net/DelayedDiskWrite.java
@@ -26,21 +26,37 @@
import java.io.FileOutputStream;
import java.io.IOException;
+/**
+ * This class provides APIs to do a delayed data write to a given {@link OutputStream}.
+ */
public class DelayedDiskWrite {
+ private static final String TAG = "DelayedDiskWrite";
+
private HandlerThread mDiskWriteHandlerThread;
private Handler mDiskWriteHandler;
/* Tracks multiple writes on the same thread */
private int mWriteSequence = 0;
- private final String TAG = "DelayedDiskWrite";
+ /**
+ * Used to do a delayed data write to a given {@link OutputStream}.
+ */
public interface Writer {
- public void onWriteCalled(DataOutputStream out) throws IOException;
+ /**
+ * write data to a given {@link OutputStream}.
+ */
+ void onWriteCalled(DataOutputStream out) throws IOException;
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w) {
write(filePath, w, true);
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w, final boolean open) {
if (TextUtils.isEmpty(filePath)) {
throw new IllegalArgumentException("empty file path");
@@ -77,7 +93,7 @@
if (out != null) {
try {
out.close();
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
// Quit if no more writes sent
diff --git a/service-t/src/com/android/server/net/InterfaceMapValue.java b/service-t/src/com/android/server/net/InterfaceMapValue.java
new file mode 100644
index 0000000..061f323
--- /dev/null
+++ b/service-t/src/com/android/server/net/InterfaceMapValue.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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 com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * The value of bpf interface index map which is used for NetworkStatsService.
+ */
+public class InterfaceMapValue extends Struct {
+ @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+ public final byte[] interfaceName;
+
+ public InterfaceMapValue(String iface) {
+ final byte[] ifaceArray = iface.getBytes();
+ interfaceName = new byte[16];
+ // All array bytes after the interface name, if any, must be 0.
+ System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length);
+ }
+}
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index bb123a3..17f3455 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -26,10 +26,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.INetd;
+import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.NetworkStats;
import android.net.UnderlyingNetworkInfo;
-import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -70,7 +70,7 @@
private final boolean mUseBpfStats;
- private final INetd mNetd;
+ private final Context mContext;
/**
* Guards persistent data access in this class
@@ -158,12 +158,12 @@
NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
}
- public NetworkStatsFactory(@NonNull INetd netd) {
- this(new File("/proc/"), true, netd);
+ public NetworkStatsFactory(@NonNull Context ctx) {
+ this(ctx, new File("/proc/"), true);
}
@VisibleForTesting
- public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) {
+ public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats) {
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
@@ -172,7 +172,7 @@
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
- mNetd = netd;
+ mContext = ctx;
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -295,11 +295,12 @@
}
@GuardedBy("mPersistentDataLock")
- private void requestSwapActiveStatsMapLocked() throws RemoteException {
- // Ask netd to do a active map stats swap. When the binder call successfully returns,
+ private void requestSwapActiveStatsMapLocked() {
+ // Do a active map stats swap. When the binder call successfully returns,
// the system server should be able to safely read and clean the inactive map
// without race problem.
- mNetd.trafficSwapActiveStatsMap();
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ cm.swapActiveStatsMap();
}
/**
@@ -327,7 +328,7 @@
if (mUseBpfStats) {
try {
requestSwapActiveStatsMapLocked();
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
throw new IOException(e);
}
// Stats are always read from the inactive map, so they must be read after the
diff --git a/service-t/src/com/android/server/net/NetworkStatsObservers.java b/service-t/src/com/android/server/net/NetworkStatsObservers.java
index b57a4f9..1953624 100644
--- a/service-t/src/com/android/server/net/NetworkStatsObservers.java
+++ b/service-t/src/com/android/server/net/NetworkStatsObservers.java
@@ -26,13 +26,12 @@
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.os.Bundle;
+import android.net.netstats.IUsageCallback;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -75,10 +74,10 @@
*
* @return the normalized request wrapped within {@link RequestInfo}.
*/
- public DataUsageRequest register(DataUsageRequest inputRequest, Messenger messenger,
- IBinder binder, int callingUid, @NetworkStatsAccess.Level int accessLevel) {
- DataUsageRequest request = buildRequest(inputRequest);
- RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
+ public DataUsageRequest register(DataUsageRequest inputRequest, IUsageCallback callback,
+ int callingUid, @NetworkStatsAccess.Level int accessLevel) {
+ DataUsageRequest request = buildRequest(inputRequest, callingUid);
+ RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid,
accessLevel);
if (LOGV) Log.v(TAG, "Registering observer for " + request);
@@ -195,10 +194,12 @@
}
}
- private DataUsageRequest buildRequest(DataUsageRequest request) {
- // Cap the minimum threshold to a safe default to avoid too many callbacks
- long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
- if (thresholdInBytes < request.thresholdInBytes) {
+ private DataUsageRequest buildRequest(DataUsageRequest request, int callingUid) {
+ // For non-system uid, cap the minimum threshold to a safe default to avoid too
+ // many callbacks.
+ long thresholdInBytes = (callingUid == Process.SYSTEM_UID ? request.thresholdInBytes
+ : Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes));
+ if (thresholdInBytes > request.thresholdInBytes) {
Log.w(TAG, "Threshold was too low for " + request
+ ". Overriding to a safer default of " + thresholdInBytes + " bytes");
}
@@ -206,11 +207,10 @@
request.template, thresholdInBytes);
}
- private RequestInfo buildRequestInfo(DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
+ private RequestInfo buildRequestInfo(DataUsageRequest request, IUsageCallback callback,
+ int callingUid, @NetworkStatsAccess.Level int accessLevel) {
if (accessLevel <= NetworkStatsAccess.Level.USER) {
- return new UserUsageRequestInfo(this, request, messenger, binder, callingUid,
+ return new UserUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
@@ -218,7 +218,7 @@
throw new IllegalArgumentException(
"accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
}
- return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
+ return new NetworkUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
}
}
@@ -230,25 +230,23 @@
private abstract static class RequestInfo implements IBinder.DeathRecipient {
private final NetworkStatsObservers mStatsObserver;
protected final DataUsageRequest mRequest;
- private final Messenger mMessenger;
- private final IBinder mBinder;
+ private final IUsageCallback mCallback;
protected final int mCallingUid;
protected final @NetworkStatsAccess.Level int mAccessLevel;
protected NetworkStatsRecorder mRecorder;
protected NetworkStatsCollection mCollection;
RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
+ IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
mStatsObserver = statsObserver;
mRequest = request;
- mMessenger = messenger;
- mBinder = binder;
+ mCallback = callback;
mCallingUid = callingUid;
mAccessLevel = accessLevel;
try {
- mBinder.linkToDeath(this, 0);
+ mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
}
@@ -257,7 +255,7 @@
@Override
public void binderDied() {
if (LOGV) {
- Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
+ Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mCallback + ")");
}
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -270,9 +268,7 @@
}
private void unlinkDeathRecipient() {
- if (mBinder != null) {
- mBinder.unlinkToDeath(this, 0);
- }
+ mCallback.asBinder().unlinkToDeath(this, 0);
}
/**
@@ -294,17 +290,19 @@
}
private void callCallback(int callbackType) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(DataUsageRequest.PARCELABLE_KEY, mRequest);
- Message msg = Message.obtain();
- msg.what = callbackType;
- msg.setData(bundle);
try {
if (LOGV) {
Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ " for " + mRequest);
}
- mMessenger.send(msg);
+ switch (callbackType) {
+ case NetworkStatsManager.CALLBACK_LIMIT_REACHED:
+ mCallback.onThresholdReached(mRequest);
+ break;
+ case NetworkStatsManager.CALLBACK_RELEASED:
+ mCallback.onCallbackReleased(mRequest);
+ break;
+ }
} catch (RemoteException e) {
// May occur naturally in the race of binder death.
Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
@@ -334,9 +332,9 @@
private static class NetworkUsageRequestInfo extends RequestInfo {
NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
+ IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, messenger, binder, callingUid, accessLevel);
+ super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override
@@ -376,9 +374,9 @@
private static class UserUsageRequestInfo extends RequestInfo {
UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- Messenger messenger, IBinder binder, int callingUid,
+ IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, messenger, binder, callingUid, accessLevel);
+ super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 9b90f3b..243d621 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -99,6 +99,7 @@
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
+import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -106,12 +107,10 @@
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -358,6 +357,9 @@
@NonNull
private final LocationPermissionChecker mLocationPermissionChecker;
+ @NonNull
+ private final BpfInterfaceMapUpdater mInterfaceMapUpdater;
+
private static @NonNull File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
@@ -420,7 +422,7 @@
final NetworkStatsService service = new NetworkStatsService(context,
INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
alarmManager, wakeLock, getDefaultClock(),
- new DefaultNetworkStatsSettings(), new NetworkStatsFactory(netd),
+ new DefaultNetworkStatsSettings(), new NetworkStatsFactory(context),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
@@ -450,11 +452,13 @@
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- new HandlerExecutor(mHandler), this);
+ (command) -> mHandler.post(command) , this);
mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor);
mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
+ mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
+ mInterfaceMapUpdater.start();
}
/**
@@ -509,6 +513,13 @@
public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
return new LocationPermissionChecker(context);
}
+
+ /** Create BpfInterfaceMapUpdater to update bpf interface map. */
+ @NonNull
+ public BpfInterfaceMapUpdater makeBpfInterfaceMapUpdater(
+ @NonNull Context ctx, @NonNull Handler handler) {
+ return new BpfInterfaceMapUpdater(ctx, handler);
+ }
}
/**
@@ -557,7 +568,7 @@
// watch for tethering changes
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
tetheringManager.registerTetheringEventCallback(
- new HandlerExecutor(mHandler), mTetherListener);
+ (command) -> mHandler.post(command), mTetherListener);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -989,8 +1000,17 @@
}
// TODO: switch to data layer stats once kernel exports
- // for now, read network layer stats and flatten across all ifaces
- final NetworkStats networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
+ // for now, read network layer stats and flatten across all ifaces.
+ // This function is used to query NeworkStats for calle's uid. The only caller method
+ // TrafficStats#getDataLayerSnapshotForUid alrady claim no special permission to query
+ // its own NetworkStats.
+ final long ident = Binder.clearCallingIdentity();
+ final NetworkStats networkLayer;
+ try {
+ networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
// splice in operation counts
networkLayer.spliceOperationsFrom(mUidOperations);
@@ -1137,21 +1157,20 @@
}
@Override
- public DataUsageRequest registerUsageCallback(String callingPackage,
- DataUsageRequest request, Messenger messenger, IBinder binder) {
+ public DataUsageRequest registerUsageCallback(@NonNull String callingPackage,
+ @NonNull DataUsageRequest request, @NonNull IUsageCallback callback) {
Objects.requireNonNull(callingPackage, "calling package is null");
Objects.requireNonNull(request, "DataUsageRequest is null");
Objects.requireNonNull(request.template, "NetworkTemplate is null");
- Objects.requireNonNull(messenger, "messenger is null");
- Objects.requireNonNull(binder, "binder is null");
+ Objects.requireNonNull(callback, "callback is null");
int callingUid = Binder.getCallingUid();
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage);
DataUsageRequest normalizedRequest;
final long token = Binder.clearCallingIdentity();
try {
- normalizedRequest = mStatsObservers.register(request, messenger, binder,
- callingUid, accessLevel);
+ normalizedRequest = mStatsObservers.register(
+ request, callback, callingUid, accessLevel);
} finally {
Binder.restoreCallingIdentity(token);
}