diff --git a/Android.mk b/Android.mk index 55ea69a7b437f..cc347676f94b3 100644 --- a/Android.mk +++ b/Android.mk @@ -238,6 +238,7 @@ LOCAL_SRC_FILES += \ core/java/android/net/INetworkScoreService.aidl \ core/java/android/net/INetworkStatsService.aidl \ core/java/android/net/INetworkStatsSession.aidl \ + core/java/android/net/ITetheringStatsProvider.aidl \ core/java/android/net/nsd/INsdManager.aidl \ core/java/android/nfc/IAppCallback.aidl \ core/java/android/nfc/INfcAdapter.aidl \ diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl new file mode 100644 index 0000000000000..769086da42b47 --- /dev/null +++ b/core/java/android/net/ITetheringStatsProvider.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 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; + +import android.net.NetworkStats; + +/** + * Interface that allows NetworkManagementService to query for tethering statistics. + * + * TODO: this does not really need to be an interface since Tethering runs in the same process + * as NetworkManagementService. Consider refactoring Tethering to use direct access to + * NetworkManagementService instead of using INetworkManagementService, and then deleting this + * interface. + * + * @hide + */ +interface ITetheringStatsProvider { + NetworkStats getTetherStats(); +} diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 92e78bc8d9779..3de217494ac52 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -20,6 +20,7 @@ package android.os; import android.net.InterfaceConfiguration; import android.net.INetd; import android.net.INetworkManagementEventObserver; +import android.net.ITetheringStatsProvider; import android.net.Network; import android.net.NetworkStats; import android.net.RouteInfo; @@ -206,6 +207,18 @@ interface INetworkManagementService */ void disableNat(String internalInterface, String externalInterface); + /** + * Registers a {@code ITetheringStatsProvider} to provide tethering statistics. + * All registered providers will be called in order, and their results will be added together. + * Netd is always registered as a tethering stats provider. + */ + void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name); + + /** + * Unregisters a previously-registered {@code ITetheringStatsProvider}. + */ + void unregisterTetheringStatsProvider(ITetheringStatsProvider provider); + /** ** PPPD **/ diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 8ea334dbfb179..7959e392d500c 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -18,6 +18,7 @@ package com.android.server; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; @@ -55,6 +56,7 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetworkManagementEventObserver; +import android.net.ITetheringStatsProvider; import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; @@ -225,6 +227,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory(); + @GuardedBy("mTetheringStatsProviders") + private final HashMap + mTetheringStatsProviders = Maps.newHashMap(); + /** * If both locks need to be held, then they should be obtained in the order: * first {@link #mQuotaLock} and then {@link #mRulesLock}. @@ -331,6 +337,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub Watchdog.getInstance().addMonitor(this); LocalServices.addService(NetworkManagementInternal.class, new LocalService()); + + synchronized (mTetheringStatsProviders) { + mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd"); + } } @VisibleForTesting @@ -520,6 +530,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + @Override + public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) { + mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); + Preconditions.checkNotNull(provider); + synchronized(mTetheringStatsProviders) { + mTetheringStatsProviders.put(provider, name); + } + } + + @Override + public void unregisterTetheringStatsProvider(ITetheringStatsProvider provider) { + mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG); + synchronized(mTetheringStatsProviders) { + mTetheringStatsProviders.remove(provider); + } + } + // Sync the state of the given chain with the native daemon. private void syncFirewallChainLocked(int chain, String name) { SparseIntArray rules; @@ -1789,14 +1816,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - @Override - public NetworkStats getNetworkStatsTethering() { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - - final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - try { - final NativeDaemonEvent[] events = mConnector.executeForList( - "bandwidth", "gettetherstats"); + private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub { + @Override + public NetworkStats getTetherStats() { + final NativeDaemonEvent[] events; + try { + events = mConnector.executeForList("bandwidth", "gettetherstats"); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); for (NativeDaemonEvent event : events) { if (event.getCode() != TetheringStatsListResult) continue; @@ -1822,8 +1851,24 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException("problem parsing tethering stats: " + event); } } - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + return stats; + } + } + + @Override + public NetworkStats getNetworkStatsTethering() { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); + synchronized (mTetheringStatsProviders) { + for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) { + try { + stats.combineAllValues(provider.getTetherStats()); + } catch (RemoteException e) { + Log.e(TAG, "Problem reading tethering stats from " + + mTetheringStatsProviders.get(provider) + ": " + e); + } + } } return stats; }