[SP03] support registerNetworkStatsProvider API am: 258eb3b9e5 am: 573e809597 am: 910f2d53bd
Change-Id: Iaedbf1f8bbe8128ca2631efff24048b97ce9d909
This commit is contained in:
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package android.app.usage;
|
package android.app.usage;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.RequiresPermission;
|
||||||
|
import android.annotation.SystemApi;
|
||||||
import android.annotation.SystemService;
|
import android.annotation.SystemService;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
import android.app.usage.NetworkStats.Bucket;
|
import android.app.usage.NetworkStats.Bucket;
|
||||||
@@ -27,6 +30,9 @@ import android.net.DataUsageRequest;
|
|||||||
import android.net.INetworkStatsService;
|
import android.net.INetworkStatsService;
|
||||||
import android.net.NetworkIdentity;
|
import android.net.NetworkIdentity;
|
||||||
import android.net.NetworkTemplate;
|
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.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -519,6 +525,34 @@ public class NetworkStatsManager {
|
|||||||
private DataUsageRequest request;
|
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) {
|
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
|
||||||
final NetworkTemplate template;
|
final NetworkTemplate template;
|
||||||
switch (networkType) {
|
switch (networkType) {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import android.net.NetworkState;
|
|||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.NetworkStatsHistory;
|
import android.net.NetworkStatsHistory;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
|
import android.net.netstats.provider.INetworkStatsProvider;
|
||||||
|
import android.net.netstats.provider.INetworkStatsProviderCallback;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import com.android.internal.net.VpnInfo;
|
import com.android.internal.net.VpnInfo;
|
||||||
@@ -89,4 +91,7 @@ interface INetworkStatsService {
|
|||||||
/** Get the total network stats information since boot */
|
/** Get the total network stats information since boot */
|
||||||
long getTotalStats(int type);
|
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.ACCESS_NETWORK_STATE;
|
||||||
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
|
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_SHUTDOWN;
|
||||||
import static android.content.Intent.ACTION_UID_REMOVED;
|
import static android.content.Intent.ACTION_UID_REMOVED;
|
||||||
import static android.content.Intent.ACTION_USER_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 static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
@@ -97,6 +99,9 @@ import android.net.NetworkStats.NonMonotonicObserver;
|
|||||||
import android.net.NetworkStatsHistory;
|
import android.net.NetworkStatsHistory;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
import android.net.TrafficStats;
|
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.BestClock;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.DropBoxManager;
|
import android.os.DropBoxManager;
|
||||||
@@ -109,6 +114,7 @@ import android.os.Looper;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
@@ -248,6 +254,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Object mStatsLock = new Object();
|
private final Object mStatsLock = new Object();
|
||||||
|
private final Object mStatsProviderLock = new Object();
|
||||||
|
|
||||||
/** Set of currently active ifaces. */
|
/** Set of currently active ifaces. */
|
||||||
@GuardedBy("mStatsLock")
|
@GuardedBy("mStatsLock")
|
||||||
@@ -272,6 +279,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
|
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
|
||||||
new DropBoxNonMonotonicObserver();
|
new DropBoxNonMonotonicObserver();
|
||||||
|
|
||||||
|
private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
|
||||||
|
new RemoteCallbackList<>();
|
||||||
|
|
||||||
@GuardedBy("mStatsLock")
|
@GuardedBy("mStatsLock")
|
||||||
private NetworkStatsRecorder mDevRecorder;
|
private NetworkStatsRecorder mDevRecorder;
|
||||||
@GuardedBy("mStatsLock")
|
@GuardedBy("mStatsLock")
|
||||||
@@ -502,9 +512,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register for a global alert that is delivered through
|
* Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
|
||||||
* {@link INetworkManagementEventObserver} once a threshold amount of data
|
* or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
|
||||||
* has been transferred.
|
* been transferred.
|
||||||
*/
|
*/
|
||||||
private void registerGlobalAlert() {
|
private void registerGlobalAlert() {
|
||||||
try {
|
try {
|
||||||
@@ -514,6 +524,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// ignored; service lives in system_server
|
// ignored; service lives in system_server
|
||||||
}
|
}
|
||||||
|
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -803,8 +814,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
@Override
|
@Override
|
||||||
public void incrementOperationCount(int uid, int tag, int operationCount) {
|
public void incrementOperationCount(int uid, int tag, int operationCount) {
|
||||||
if (Binder.getCallingUid() != uid) {
|
if (Binder.getCallingUid() != uid) {
|
||||||
mContext.enforceCallingOrSelfPermission(
|
mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
|
||||||
android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operationCount < 0) {
|
if (operationCount < 0) {
|
||||||
@@ -1095,7 +1105,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
/**
|
/**
|
||||||
* Observer that watches for {@link INetworkManagementService} alerts.
|
* Observer that watches for {@link INetworkManagementService} alerts.
|
||||||
*/
|
*/
|
||||||
private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
|
private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
|
||||||
@Override
|
@Override
|
||||||
public void limitReached(String limitName, String iface) {
|
public void limitReached(String limitName, String iface) {
|
||||||
// only someone like NMS should be calling us
|
// only someone like NMS should be calling us
|
||||||
@@ -1252,6 +1262,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
xtSnapshot.combineAllValues(tetherSnapshot);
|
xtSnapshot.combineAllValues(tetherSnapshot);
|
||||||
devSnapshot.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
|
// 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.
|
// can't be reattributed to responsible apps.
|
||||||
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
|
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
|
||||||
@@ -1355,6 +1373,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
performSampleLocked();
|
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
|
// finally, dispatch updated event to any listeners
|
||||||
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
|
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
|
||||||
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
|
||||||
@@ -1476,6 +1498,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
public void forceUpdate() {
|
public void forceUpdate() {
|
||||||
NetworkStatsService.this.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
|
@Override
|
||||||
@@ -1690,6 +1718,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
|
|||||||
uidSnapshot.combineAllValues(vtStats);
|
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);
|
uidSnapshot.combineAllValues(mUidOperations);
|
||||||
|
|
||||||
return uidSnapshot;
|
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
|
@VisibleForTesting
|
||||||
static class HandlerCallback implements Handler.Callback {
|
static class HandlerCallback implements Handler.Callback {
|
||||||
private final NetworkStatsService mService;
|
private final NetworkStatsService mService;
|
||||||
|
|||||||
Reference in New Issue
Block a user