[SP03] support registerNetworkStatsProvider API
This change provides an API that allow external modules to register a custom provider of NetworkStats to merge the network statistics that cannot be seen by the kernel to system. Test: atest FrameworksNetTests CtsUsageStatsTestCases Test: atest NetworkPolicyManagerServiceTest Test: m doc-comment-check-docs Bug: 130855321 Change-Id: I265bc637c40666cde505fde5056d2d9bfc5fb204
This commit is contained in:
@@ -16,7 +16,10 @@
|
||||
|
||||
package android.app.usage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.usage.NetworkStats.Bucket;
|
||||
@@ -27,6 +30,9 @@ import android.net.DataUsageRequest;
|
||||
import android.net.INetworkStatsService;
|
||||
import android.net.NetworkIdentity;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.net.netstats.provider.AbstractNetworkStatsProvider;
|
||||
import android.net.netstats.provider.NetworkStatsProviderCallback;
|
||||
import android.net.netstats.provider.NetworkStatsProviderWrapper;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -519,6 +525,34 @@ public class NetworkStatsManager {
|
||||
private DataUsageRequest request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom provider of {@link android.net.NetworkStats} to combine the network
|
||||
* statistics that cannot be seen by the kernel to system. To unregister, invoke
|
||||
* {@link NetworkStatsProviderCallback#unregister()}.
|
||||
*
|
||||
* @param tag a human readable identifier of the custom network stats provider.
|
||||
* @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to
|
||||
* be registered to the system.
|
||||
* @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
|
||||
* system.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
|
||||
@NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
|
||||
@NonNull String tag,
|
||||
@NonNull AbstractNetworkStatsProvider provider) {
|
||||
try {
|
||||
final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
|
||||
return new NetworkStatsProviderCallback(
|
||||
mService.registerNetworkStatsProvider(tag, wrapper));
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowAsRuntimeException();
|
||||
}
|
||||
// Unreachable code, but compiler doesn't know about it.
|
||||
return null;
|
||||
}
|
||||
|
||||
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
|
||||
final NetworkTemplate template;
|
||||
switch (networkType) {
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.net.NetworkState;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.net.netstats.provider.INetworkStatsProvider;
|
||||
import android.net.netstats.provider.INetworkStatsProviderCallback;
|
||||
import android.os.IBinder;
|
||||
import android.os.Messenger;
|
||||
import com.android.internal.net.VpnInfo;
|
||||
@@ -89,4 +91,7 @@ interface INetworkStatsService {
|
||||
/** Get the total network stats information since boot */
|
||||
long getTotalStats(int type);
|
||||
|
||||
/** Registers a network stats provider */
|
||||
INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
|
||||
in INetworkStatsProvider provider);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.net;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
|
||||
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
|
||||
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
|
||||
import static android.content.Intent.ACTION_SHUTDOWN;
|
||||
import static android.content.Intent.ACTION_UID_REMOVED;
|
||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||
@@ -71,6 +72,7 @@ import static com.android.server.NetworkManagementSocketTagger.resetKernelUidSta
|
||||
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.usage.NetworkStatsManager;
|
||||
@@ -97,6 +99,9 @@ import android.net.NetworkStats.NonMonotonicObserver;
|
||||
import android.net.NetworkStatsHistory;
|
||||
import android.net.NetworkTemplate;
|
||||
import android.net.TrafficStats;
|
||||
import android.net.netstats.provider.INetworkStatsProvider;
|
||||
import android.net.netstats.provider.INetworkStatsProviderCallback;
|
||||
import android.net.netstats.provider.NetworkStatsProviderCallback;
|
||||
import android.os.BestClock;
|
||||
import android.os.Binder;
|
||||
import android.os.DropBoxManager;
|
||||
@@ -109,6 +114,7 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
@@ -248,6 +254,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
}
|
||||
|
||||
private final Object mStatsLock = new Object();
|
||||
private final Object mStatsProviderLock = new Object();
|
||||
|
||||
/** Set of currently active ifaces. */
|
||||
@GuardedBy("mStatsLock")
|
||||
@@ -272,6 +279,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
|
||||
new DropBoxNonMonotonicObserver();
|
||||
|
||||
private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
|
||||
new RemoteCallbackList<>();
|
||||
|
||||
@GuardedBy("mStatsLock")
|
||||
private NetworkStatsRecorder mDevRecorder;
|
||||
@GuardedBy("mStatsLock")
|
||||
@@ -502,9 +512,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register for a global alert that is delivered through
|
||||
* {@link INetworkManagementEventObserver} once a threshold amount of data
|
||||
* has been transferred.
|
||||
* Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
|
||||
* or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
|
||||
* been transferred.
|
||||
*/
|
||||
private void registerGlobalAlert() {
|
||||
try {
|
||||
@@ -514,6 +524,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
} catch (RemoteException e) {
|
||||
// ignored; service lives in system_server
|
||||
}
|
||||
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -803,8 +814,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
@Override
|
||||
public void incrementOperationCount(int uid, int tag, int operationCount) {
|
||||
if (Binder.getCallingUid() != uid) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
|
||||
mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
|
||||
}
|
||||
|
||||
if (operationCount < 0) {
|
||||
@@ -1095,7 +1105,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
/**
|
||||
* Observer that watches for {@link INetworkManagementService} alerts.
|
||||
*/
|
||||
private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
|
||||
private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
|
||||
@Override
|
||||
public void limitReached(String limitName, String iface) {
|
||||
// only someone like NMS should be calling us
|
||||
@@ -1252,6 +1262,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
xtSnapshot.combineAllValues(tetherSnapshot);
|
||||
devSnapshot.combineAllValues(tetherSnapshot);
|
||||
|
||||
// Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data
|
||||
// from stats providers that isn't already counted by dev and XT stats.
|
||||
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider");
|
||||
final NetworkStats providersnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
|
||||
Trace.traceEnd(TRACE_TAG_NETWORK);
|
||||
xtSnapshot.combineAllValues(providersnapshot);
|
||||
devSnapshot.combineAllValues(providersnapshot);
|
||||
|
||||
// For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
|
||||
// can't be reattributed to responsible apps.
|
||||
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
|
||||
@@ -1355,6 +1373,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
performSampleLocked();
|
||||
}
|
||||
|
||||
// request asynchronous stats update from all providers for next poll.
|
||||
// TODO: request with a valid token.
|
||||
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
|
||||
|
||||
// finally, dispatch updated event to any listeners
|
||||
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
|
||||
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
||||
@@ -1476,6 +1498,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
public void forceUpdate() {
|
||||
NetworkStatsService.this.forceUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatsProviderLimit(@NonNull String iface, long quota) {
|
||||
Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")");
|
||||
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1690,6 +1718,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
uidSnapshot.combineAllValues(vtStats);
|
||||
}
|
||||
|
||||
// get a stale copy of uid stats snapshot provided by providers.
|
||||
final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
|
||||
providerStats.filter(UID_ALL, ifaces, TAG_ALL);
|
||||
mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats);
|
||||
uidSnapshot.combineAllValues(providerStats);
|
||||
|
||||
uidSnapshot.combineAllValues(mUidOperations);
|
||||
|
||||
return uidSnapshot;
|
||||
@@ -1726,6 +1760,152 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom provider of {@link android.net.NetworkStats} to combine the network
|
||||
* statistics that cannot be seen by the kernel to system. To unregister, invoke the
|
||||
* {@code unregister()} of the returned callback.
|
||||
*
|
||||
* @param tag a human readable identifier of the custom network stats provider.
|
||||
* @param provider the binder interface of
|
||||
* {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that
|
||||
* needs to be registered to the system.
|
||||
*
|
||||
* @return a binder interface of
|
||||
* {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
|
||||
* used to report events to the system.
|
||||
*/
|
||||
public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
|
||||
@NonNull String tag, @NonNull INetworkStatsProvider provider) {
|
||||
mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
|
||||
Objects.requireNonNull(provider, "provider is null");
|
||||
Objects.requireNonNull(tag, "tag is null");
|
||||
try {
|
||||
NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
|
||||
tag, provider, mAlertObserver, mStatsProviderCbList);
|
||||
mStatsProviderCbList.register(callback);
|
||||
Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
|
||||
+ getCallingUid() + "/" + getCallingPid());
|
||||
return callback;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "registerNetworkStatsProvider failed", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Collect stats from local cache of providers.
|
||||
private @NonNull NetworkStats getNetworkStatsFromProviders(int how) {
|
||||
final NetworkStats ret = new NetworkStats(0L, 0);
|
||||
invokeForAllStatsProviderCallbacks((cb) -> ret.combineAllValues(cb.getCachedStats(how)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingConsumer<S, T extends Throwable> {
|
||||
void accept(S s) throws T;
|
||||
}
|
||||
|
||||
private void invokeForAllStatsProviderCallbacks(
|
||||
@NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
|
||||
synchronized (mStatsProviderCbList) {
|
||||
final int length = mStatsProviderCbList.beginBroadcast();
|
||||
try {
|
||||
for (int i = 0; i < length; i++) {
|
||||
final NetworkStatsProviderCallbackImpl cb =
|
||||
mStatsProviderCbList.getBroadcastItem(i);
|
||||
try {
|
||||
task.accept(cb);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
mStatsProviderCbList.finishBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub
|
||||
implements IBinder.DeathRecipient {
|
||||
@NonNull final String mTag;
|
||||
@NonNull private final Object mProviderStatsLock = new Object();
|
||||
@NonNull final INetworkStatsProvider mProvider;
|
||||
@NonNull final INetworkManagementEventObserver mAlertObserver;
|
||||
@NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
|
||||
|
||||
@GuardedBy("mProviderStatsLock")
|
||||
// STATS_PER_IFACE and STATS_PER_UID
|
||||
private final NetworkStats mIfaceStats = new NetworkStats(0L, 0);
|
||||
@GuardedBy("mProviderStatsLock")
|
||||
private final NetworkStats mUidStats = new NetworkStats(0L, 0);
|
||||
|
||||
NetworkStatsProviderCallbackImpl(
|
||||
@NonNull String tag, @NonNull INetworkStatsProvider provider,
|
||||
@NonNull INetworkManagementEventObserver alertObserver,
|
||||
@NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList)
|
||||
throws RemoteException {
|
||||
mTag = tag;
|
||||
mProvider = provider;
|
||||
mProvider.asBinder().linkToDeath(this, 0);
|
||||
mAlertObserver = alertObserver;
|
||||
mStatsProviderCbList = cbList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public NetworkStats getCachedStats(int how) {
|
||||
synchronized (mProviderStatsLock) {
|
||||
NetworkStats stats;
|
||||
switch (how) {
|
||||
case STATS_PER_IFACE:
|
||||
stats = mIfaceStats;
|
||||
break;
|
||||
case STATS_PER_UID:
|
||||
stats = mUidStats;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid type: " + how);
|
||||
}
|
||||
// Return a defensive copy instead of local reference.
|
||||
return stats.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
|
||||
@Nullable NetworkStats uidStats) {
|
||||
// TODO: 1. Use token to map ifaces to correct NetworkIdentity.
|
||||
// 2. Store the difference and store it directly to the recorder.
|
||||
synchronized (mProviderStatsLock) {
|
||||
if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats);
|
||||
if (uidStats != null) mUidStats.combineAllValues(uidStats);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlertReached() throws RemoteException {
|
||||
mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLimitReached() {
|
||||
Log.d(TAG, mTag + ": onLimitReached");
|
||||
LocalServices.getService(NetworkPolicyManagerInternal.class)
|
||||
.onStatsProviderLimitReached(mTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.d(TAG, mTag + ": binderDied");
|
||||
mStatsProviderCbList.unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
Log.d(TAG, mTag + ": unregister");
|
||||
mStatsProviderCbList.unregister(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class HandlerCallback implements Handler.Callback {
|
||||
private final NetworkStatsService mService;
|
||||
|
||||
Reference in New Issue
Block a user