[MS08] Move NetworkStats files to f/b/package/ConnectivityT

NetworkStatsService is going to be moved into ConnectivityService
module. Move all related files to packages/ConnectivityT so that
it can be easily migrate these files to connectivity module
after clearing the hidden API usages.

Bug: 197717846
Test: TH
Change-Id: Iead00832b5eb7b1dc40a92027c5a14ae8316b16c
This commit is contained in:
Junyu Lai
2021-12-07 08:25:31 +00:00
parent 96fe4ce495
commit ad166b43df
30 changed files with 98 additions and 6 deletions

View File

@@ -0,0 +1,717 @@
/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package android.app.usage;
import android.annotation.IntDef;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
import android.util.IntArray;
import android.util.Log;
import dalvik.system.CloseGuard;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
* are returned as results to various queries in {@link NetworkStatsManager}.
*/
public final class NetworkStats implements AutoCloseable {
private final static String TAG = "NetworkStats";
private final CloseGuard mCloseGuard = CloseGuard.get();
/**
* Start timestamp of stats collected
*/
private final long mStartTimeStamp;
/**
* End timestamp of stats collected
*/
private final long mEndTimeStamp;
/**
* Non-null array indicates the query enumerates over uids.
*/
private int[] mUids;
/**
* Index of the current uid in mUids when doing uid enumeration or a single uid value,
* depending on query type.
*/
private int mUidOrUidIndex;
/**
* Tag id in case if was specified in the query.
*/
private int mTag = android.net.NetworkStats.TAG_NONE;
/**
* State in case it was not specified in the query.
*/
private int mState = Bucket.STATE_ALL;
/**
* The session while the query requires it, null if all the stats have been collected or close()
* has been called.
*/
private INetworkStatsSession mSession;
private NetworkTemplate mTemplate;
/**
* Results of a summary query.
*/
private android.net.NetworkStats mSummary = null;
/**
* Results of detail queries.
*/
private NetworkStatsHistory mHistory = null;
/**
* Where we are in enumerating over the current result.
*/
private int mEnumerationIndex = 0;
/**
* Recycling entry objects to prevent heap fragmentation.
*/
private android.net.NetworkStats.Entry mRecycledSummaryEntry = null;
private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
/** @hide */
NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
long endTimestamp, INetworkStatsService statsService)
throws RemoteException, SecurityException {
// Open network stats session
mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
mCloseGuard.open("close");
mTemplate = template;
mStartTimeStamp = startTimestamp;
mEndTimeStamp = endTimestamp;
}
@Override
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
close();
} finally {
super.finalize();
}
}
// -------------------------BEGINNING OF PUBLIC API-----------------------------------
/**
* Buckets are the smallest elements of a query result. As some dimensions of a result may be
* aggregated (e.g. time or state) some values may be equal across all buckets.
*/
public static class Bucket {
/** @hide */
@IntDef(prefix = { "STATE_" }, value = {
STATE_ALL,
STATE_DEFAULT,
STATE_FOREGROUND
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {}
/**
* Combined usage across all states.
*/
public static final int STATE_ALL = -1;
/**
* Usage not accounted for in any other state.
*/
public static final int STATE_DEFAULT = 0x1;
/**
* Foreground usage.
*/
public static final int STATE_FOREGROUND = 0x2;
/**
* Special UID value for aggregate/unspecified.
*/
public static final int UID_ALL = android.net.NetworkStats.UID_ALL;
/**
* Special UID value for removed apps.
*/
public static final int UID_REMOVED = TrafficStats.UID_REMOVED;
/**
* Special UID value for data usage by tethering.
*/
public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
/** @hide */
@IntDef(prefix = { "METERED_" }, value = {
METERED_ALL,
METERED_NO,
METERED_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface Metered {}
/**
* Combined usage across all metered states. Covers metered and unmetered usage.
*/
public static final int METERED_ALL = -1;
/**
* Usage that occurs on an unmetered network.
*/
public static final int METERED_NO = 0x1;
/**
* Usage that occurs on a metered network.
*
* <p>A network is classified as metered when the user is sensitive to heavy data usage on
* that connection.
*/
public static final int METERED_YES = 0x2;
/** @hide */
@IntDef(prefix = { "ROAMING_" }, value = {
ROAMING_ALL,
ROAMING_NO,
ROAMING_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface Roaming {}
/**
* Combined usage across all roaming states. Covers both roaming and non-roaming usage.
*/
public static final int ROAMING_ALL = -1;
/**
* Usage that occurs on a home, non-roaming network.
*
* <p>Any cellular usage in this bucket was incurred while the device was connected to a
* tower owned or operated by the user's wireless carrier, or a tower that the user's
* wireless carrier has indicated should be treated as a home network regardless.
*
* <p>This is also the default value for network types that do not support roaming.
*/
public static final int ROAMING_NO = 0x1;
/**
* Usage that occurs on a roaming network.
*
* <p>Any cellular usage in this bucket as incurred while the device was roaming on another
* carrier's network, for which additional charges may apply.
*/
public static final int ROAMING_YES = 0x2;
/** @hide */
@IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
DEFAULT_NETWORK_ALL,
DEFAULT_NETWORK_NO,
DEFAULT_NETWORK_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface DefaultNetworkStatus {}
/**
* Combined usage for this network regardless of default network status.
*/
public static final int DEFAULT_NETWORK_ALL = -1;
/**
* Usage that occurs while this network is not a default network.
*
* <p>This implies that the app responsible for this usage requested that it occur on a
* specific network different from the one(s) the system would have selected for it.
*/
public static final int DEFAULT_NETWORK_NO = 0x1;
/**
* Usage that occurs while this network is a default network.
*
* <p>This implies that the app either did not select a specific network for this usage,
* or it selected a network that the system could have selected for app traffic.
*/
public static final int DEFAULT_NETWORK_YES = 0x2;
/**
* Special TAG value for total data across all tags
*/
public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE;
private int mUid;
private int mTag;
private int mState;
private int mDefaultNetworkStatus;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
private long mEndTimeStamp;
private long mRxBytes;
private long mRxPackets;
private long mTxBytes;
private long mTxPackets;
private static int convertSet(@State int state) {
switch (state) {
case STATE_ALL: return android.net.NetworkStats.SET_ALL;
case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND;
}
return 0;
}
private static @State int convertState(int networkStatsSet) {
switch (networkStatsSet) {
case android.net.NetworkStats.SET_ALL : return STATE_ALL;
case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
}
return 0;
}
private static int convertUid(int uid) {
switch (uid) {
case TrafficStats.UID_REMOVED: return UID_REMOVED;
case TrafficStats.UID_TETHERING: return UID_TETHERING;
}
return uid;
}
private static int convertTag(int tag) {
switch (tag) {
case android.net.NetworkStats.TAG_NONE: return TAG_NONE;
}
return tag;
}
private static @Metered int convertMetered(int metered) {
switch (metered) {
case android.net.NetworkStats.METERED_ALL : return METERED_ALL;
case android.net.NetworkStats.METERED_NO: return METERED_NO;
case android.net.NetworkStats.METERED_YES: return METERED_YES;
}
return 0;
}
private static @Roaming int convertRoaming(int roaming) {
switch (roaming) {
case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
}
return 0;
}
private static @DefaultNetworkStatus int convertDefaultNetworkStatus(
int defaultNetworkStatus) {
switch (defaultNetworkStatus) {
case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
}
return 0;
}
public Bucket() {
}
/**
* Key of the bucket. Usually an app uid or one of the following special values:<p />
* <ul>
* <li>{@link #UID_REMOVED}</li>
* <li>{@link #UID_TETHERING}</li>
* <li>{@link android.os.Process#SYSTEM_UID}</li>
* </ul>
* @return Bucket key.
*/
public int getUid() {
return mUid;
}
/**
* Tag of the bucket.<p />
* @return Bucket tag.
*/
public int getTag() {
return mTag;
}
/**
* Usage state. One of the following values:<p/>
* <ul>
* <li>{@link #STATE_ALL}</li>
* <li>{@link #STATE_DEFAULT}</li>
* <li>{@link #STATE_FOREGROUND}</li>
* </ul>
* @return Usage state.
*/
public @State int getState() {
return mState;
}
/**
* Metered state. One of the following values:<p/>
* <ul>
* <li>{@link #METERED_ALL}</li>
* <li>{@link #METERED_NO}</li>
* <li>{@link #METERED_YES}</li>
* </ul>
* <p>A network is classified as metered when the user is sensitive to heavy data usage on
* that connection. Apps may warn before using these networks for large downloads. The
* metered state can be set by the user within data usage network restrictions.
*/
public @Metered int getMetered() {
return mMetered;
}
/**
* Roaming state. One of the following values:<p/>
* <ul>
* <li>{@link #ROAMING_ALL}</li>
* <li>{@link #ROAMING_NO}</li>
* <li>{@link #ROAMING_YES}</li>
* </ul>
*/
public @Roaming int getRoaming() {
return mRoaming;
}
/**
* Default network status. One of the following values:<p/>
* <ul>
* <li>{@link #DEFAULT_NETWORK_ALL}</li>
* <li>{@link #DEFAULT_NETWORK_NO}</li>
* <li>{@link #DEFAULT_NETWORK_YES}</li>
* </ul>
*/
public @DefaultNetworkStatus int getDefaultNetworkStatus() {
return mDefaultNetworkStatus;
}
/**
* Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Start of interval.
*/
public long getStartTimeStamp() {
return mBeginTimeStamp;
}
/**
* End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return End of interval.
*/
public long getEndTimeStamp() {
return mEndTimeStamp;
}
/**
* Number of bytes received during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of bytes.
*/
public long getRxBytes() {
return mRxBytes;
}
/**
* Number of bytes transmitted during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of bytes.
*/
public long getTxBytes() {
return mTxBytes;
}
/**
* Number of packets received during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of packets.
*/
public long getRxPackets() {
return mRxPackets;
}
/**
* Number of packets transmitted during the bucket's time interval. Statistics are measured
* at the network layer, so they include both TCP and UDP usage.
* @return Number of packets.
*/
public long getTxPackets() {
return mTxPackets;
}
}
/**
* Fills the recycled bucket with data of the next bin in the enumeration.
* @param bucketOut Bucket to be filled with data.
* @return true if successfully filled the bucket, false otherwise.
*/
public boolean getNextBucket(Bucket bucketOut) {
if (mSummary != null) {
return getNextSummaryBucket(bucketOut);
} else {
return getNextHistoryBucket(bucketOut);
}
}
/**
* Check if it is possible to ask for a next bucket in the enumeration.
* @return true if there is at least one more bucket.
*/
public boolean hasNextBucket() {
if (mSummary != null) {
return mEnumerationIndex < mSummary.size();
} else if (mHistory != null) {
return mEnumerationIndex < mHistory.size()
|| hasNextUid();
}
return false;
}
/**
* Closes the enumeration. Call this method before this object gets out of scope.
*/
@Override
public void close() {
if (mSession != null) {
try {
mSession.close();
} catch (RemoteException e) {
Log.w(TAG, e);
// Otherwise, meh
}
}
mSession = null;
if (mCloseGuard != null) {
mCloseGuard.close();
}
}
// -------------------------END OF PUBLIC API-----------------------------------
/**
* Collects device summary results into a Bucket.
* @throws RemoteException
*/
Bucket getDeviceSummaryForNetwork() throws RemoteException {
mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
// Setting enumeration index beyond end to avoid accidental enumeration over data that does
// not belong to the calling user.
mEnumerationIndex = mSummary.size();
return getSummaryAggregate();
}
/**
* Collects summary results and sets summary enumeration mode.
* @throws RemoteException
*/
void startSummaryEnumeration() throws RemoteException {
mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp,
false /* includeTags */);
mEnumerationIndex = 0;
}
/**
* Collects history results for uid and resets history enumeration index.
*/
void startHistoryEnumeration(int uid, int tag, int state) {
mHistory = null;
try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL,
mStartTimeStamp, mEndTimeStamp);
setSingleUidTagState(uid, tag, state);
} catch (RemoteException e) {
Log.w(TAG, e);
// Leaving mHistory null
}
mEnumerationIndex = 0;
}
/**
* Starts uid enumeration for current user.
* @throws RemoteException
*/
void startUserUidEnumeration() throws RemoteException {
// TODO: getRelevantUids should be sensitive to time interval. When that's done,
// the filtering logic below can be removed.
int[] uids = mSession.getRelevantUids();
// Filtering of uids with empty history.
IntArray filteredUids = new IntArray(uids.length);
for (int uid : uids) {
try {
NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
if (history != null && history.size() > 0) {
filteredUids.add(uid);
}
} catch (RemoteException e) {
Log.w(TAG, "Error while getting history of uid " + uid, e);
}
}
mUids = filteredUids.toArray();
mUidOrUidIndex = -1;
stepHistory();
}
/**
* Steps to next uid in enumeration and collects history for that.
*/
private void stepHistory(){
if (hasNextUid()) {
stepUid();
mHistory = null;
try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
} catch (RemoteException e) {
Log.w(TAG, e);
// Leaving mHistory null
}
mEnumerationIndex = 0;
}
}
private void fillBucketFromSummaryEntry(Bucket bucketOut) {
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus(
mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
bucketOut.mBeginTimeStamp = mStartTimeStamp;
bucketOut.mEndTimeStamp = mEndTimeStamp;
bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
}
/**
* Getting the next item in summary enumeration.
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
private boolean getNextSummaryBucket(Bucket bucketOut) {
if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
fillBucketFromSummaryEntry(bucketOut);
return true;
}
return false;
}
Bucket getSummaryAggregate() {
if (mSummary == null) {
return null;
}
Bucket bucket = new Bucket();
if (mRecycledSummaryEntry == null) {
mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
}
mSummary.getTotal(mRecycledSummaryEntry);
fillBucketFromSummaryEntry(bucket);
return bucket;
}
/**
* Getting the next item in a history enumeration.
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
private boolean getNextHistoryBucket(Bucket bucketOut) {
if (bucketOut != null && mHistory != null) {
if (mEnumerationIndex < mHistory.size()) {
mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
mRecycledHistoryEntry);
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = mState;
bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
mRecycledHistoryEntry.bucketDuration;
bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
return true;
} else if (hasNextUid()) {
stepHistory();
return getNextHistoryBucket(bucketOut);
}
}
return false;
}
// ------------------ UID LOGIC------------------------
private boolean isUidEnumeration() {
return mUids != null;
}
private boolean hasNextUid() {
return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
}
private int getUid() {
// Check if uid enumeration.
if (isUidEnumeration()) {
if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
throw new IndexOutOfBoundsException(
"Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
}
return mUids[mUidOrUidIndex];
}
// Single uid mode.
return mUidOrUidIndex;
}
private void setSingleUidTagState(int uid, int tag, int state) {
mUidOrUidIndex = uid;
mTag = tag;
mState = state;
}
private void stepUid() {
if (mUids != null) {
++mUidOrUidIndex;
}
}
}

View File

@@ -0,0 +1,767 @@
/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package android.app.usage;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
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.annotation.WorkerThread;
import android.app.usage.NetworkStats.Bucket;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
import android.net.Network;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
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.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DataUnit;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetworkIdentityUtils;
import java.util.List;
import java.util.Objects;
/**
* Provides access to network usage history and statistics. Usage data is collected in
* discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
* <p />
* Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and
* Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain
* data about themselves. See the below note for special cases in which apps can obtain data about
* other applications.
* <h3>
* Summary queries
* </h3>
* {@link #querySummaryForDevice} <p />
* {@link #querySummaryForUser} <p />
* {@link #querySummary} <p />
* These queries aggregate network usage across the whole interval. Therefore there will be only one
* bucket for a particular key, state, metered and roaming combination. In case of the user-wide
* and device-wide summaries a single bucket containing the totalised network usage is returned.
* <h3>
* History queries
* </h3>
* {@link #queryDetailsForUid} <p />
* {@link #queryDetails} <p />
* These queries do not aggregate over time but do aggregate over state, metered and roaming.
* Therefore there can be multiple buckets for a particular key. However, all Buckets will have
* {@code state} {@link NetworkStats.Bucket#STATE_ALL},
* {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
* {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p />
* <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
* calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
* which is a system-level permission and will not be granted to third-party apps. However,
* declaring the permission implies intention to use the API and the user of the device can grant
* permission through the Settings application.
* <p />
* Profile owner apps are automatically granted permission to query data on the profile they manage
* (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier-
* privileged apps likewise get access to usage data for all users on the device.
* <p />
* In addition to tethering usage, usage by removed users and apps, and usage by the system
* is also included in the results for callers with one of these higher levels of access.
* <p />
* <b>NOTE:</b> Prior to API level {@value android.os.Build.VERSION_CODES#N}, all calls to these APIs required
* the above permission, even to access an app's own data usage, and carrier-privileged apps were
* not included.
*/
@SystemService(Context.NETWORK_STATS_SERVICE)
public class NetworkStatsManager {
private static final String TAG = "NetworkStatsManager";
private static final boolean DBG = false;
/** @hide */
public static final int CALLBACK_LIMIT_REACHED = 0;
/** @hide */
public static final int CALLBACK_RELEASED = 1;
/**
* Minimum data usage threshold for registering usage callbacks.
*
* Requests registered with a threshold lower than this will only be triggered once this minimum
* is reached.
* @hide
*/
public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
private final Context mContext;
private final INetworkStatsService mService;
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
/** @hide */
public static final int FLAG_POLL_FORCE = 1 << 1;
/** @hide */
public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 2;
private int mFlags;
/**
* {@hide}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public NetworkStatsManager(Context context) throws ServiceNotFoundException {
this(context, INetworkStatsService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
}
/** @hide */
@VisibleForTesting
public NetworkStatsManager(Context context, INetworkStatsService service) {
mContext = context;
mService = service;
setPollOnOpen(true);
}
/** @hide */
public void setPollOnOpen(boolean pollOnOpen) {
if (pollOnOpen) {
mFlags |= FLAG_POLL_ON_OPEN;
} else {
mFlags &= ~FLAG_POLL_ON_OPEN;
}
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
public void setPollForce(boolean pollForce) {
if (pollForce) {
mFlags |= FLAG_POLL_FORCE;
} else {
mFlags &= ~FLAG_POLL_FORCE;
}
}
/** @hide */
public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) {
if (augmentWithSubscriptionPlan) {
mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
} else {
mFlags &= ~FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
}
}
/** @hide */
public Bucket querySummaryForDevice(NetworkTemplate template,
long startTime, long endTime) throws SecurityException, RemoteException {
Bucket bucket = null;
NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
mService);
bucket = stats.getDeviceSummaryForNetwork();
stats.close();
return bucket;
}
/**
* Query network usage statistics summaries. Result is summarised data usage for the whole
* device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
* roaming. This means the bucket's start and end timestamp are going to be the same as the
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
* tag {@link NetworkStats.Bucket#TAG_NONE},
* default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered {@link NetworkStats.Bucket#METERED_ALL},
* and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
@WorkerThread
public Bucket querySummaryForDevice(int networkType, String subscriberId,
long startTime, long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
template = createTemplate(networkType, subscriberId);
} catch (IllegalArgumentException e) {
if (DBG) Log.e(TAG, "Cannot create template", e);
return null;
}
return querySummaryForDevice(template, startTime, endTime);
}
/**
* Query network usage statistics summaries. Result is summarised data usage for all uids
* belonging to calling user. Result is a single Bucket aggregated over time, state and uid.
* This means the bucket's start and end timestamp are going to be the same as the 'startTime'
* and 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL},
* uid {@link NetworkStats.Bucket#UID_ALL}, tag {@link NetworkStats.Bucket#TAG_NONE},
* metered {@link NetworkStats.Bucket#METERED_ALL}, and roaming
* {@link NetworkStats.Bucket#ROAMING_ALL}.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Bucket object or null if permissions are insufficient or error happened during
* statistics collection.
*/
@WorkerThread
public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
template = createTemplate(networkType, subscriberId);
} catch (IllegalArgumentException e) {
if (DBG) Log.e(TAG, "Cannot create template", e);
return null;
}
NetworkStats stats;
stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
stats.startSummaryEnumeration();
stats.close();
return stats.getSummaryAggregate();
}
/**
* Query network usage statistics summaries. Result filtered to include only uids belonging to
* calling user. Result is aggregated over time, hence all buckets will have the same start and
* end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
* means buckets' start and end timestamps are going to be the same as the 'startTime' and
* 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
* be the same.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
@WorkerThread
public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
template = createTemplate(networkType, subscriberId);
} catch (IllegalArgumentException e) {
if (DBG) Log.e(TAG, "Cannot create template", e);
return null;
}
return querySummary(template, startTime, endTime);
}
/** @hide */
public NetworkStats querySummary(NetworkTemplate template, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkStats result;
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startSummaryEnumeration();
return result;
}
/**
* Query network usage statistics details for a given uid.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
@WorkerThread
public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
}
/** @hide */
public NetworkStats queryDetailsForUid(NetworkTemplate template,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(template, startTime, endTime, uid,
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
}
/**
* Query network usage statistics details for a given uid and tag.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
@WorkerThread
public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
tag, NetworkStats.Bucket.STATE_ALL);
}
/**
* Query network usage statistics details for a given uid, tag, and state. Only usable for uids
* belonging to calling user. Result is not aggregated over time. This means buckets' start and
* end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going
* to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state
* the same as the 'state' parameter.
* defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
* roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param uid UID of app
* @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
* @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
* traffic from all states.
* @return Statistics object or null if an error happened during statistics collection.
* @throws SecurityException if permissions are insufficient to read network statistics.
*/
@WorkerThread
public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkTemplate template;
template = createTemplate(networkType, subscriberId);
return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
}
/** @hide */
public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkStats result;
try {
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startHistoryEnumeration(uid, tag, state);
} catch (RemoteException e) {
Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
+ " state=" + state, e);
return null;
}
return result;
}
/**
* Query network usage statistics details. Result filtered to include only uids belonging to
* calling user. Result is aggregated over state but not aggregated over time, uid, tag,
* metered, nor roaming. This means buckets' start and end timestamps are going to be between
* 'startTime' and 'endTime' parameters. State is going to be
* {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
* tag {@link NetworkStats.Bucket#TAG_NONE},
* default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
* and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
* <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets. Since bucket length is in the order of hours, this
* method cannot be used to measure data usage on a fine grained time scale.
* This may take a long time, and apps should avoid calling this on their main thread.
*
* @param networkType As defined in {@link ConnectivityManager}, e.g.
* {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
* etc.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when querying for the mobile network type to receive usage
* for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param startTime Start of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Statistics object or null if permissions are insufficient or error happened during
* statistics collection.
*/
@WorkerThread
public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
template = createTemplate(networkType, subscriberId);
} catch (IllegalArgumentException e) {
if (DBG) Log.e(TAG, "Cannot create template", e);
return null;
}
NetworkStats result;
result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
result.startUserUidEnumeration();
return result;
}
/** @hide */
public void registerUsageCallback(NetworkTemplate template, int networkType,
long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
Objects.requireNonNull(callback, "UsageCallback cannot be null");
final Looper looper;
if (handler == null) {
looper = Looper.myLooper();
} else {
looper = handler.getLooper();
}
DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
template, thresholdBytes);
try {
CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
template.getSubscriberId(), callback);
callback.request = mService.registerUsageCallback(
mContext.getOpPackageName(), request, new Messenger(callbackHandler),
new Binder());
if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
if (callback.request == null) {
Log.e(TAG, "Request from callback is null; should not happen");
}
} catch (RemoteException e) {
if (DBG) Log.d(TAG, "Remote exception when registering callback");
throw e.rethrowFromSystemServer();
}
}
/**
* Registers to receive notifications about data usage on specified networks.
*
* @see #registerUsageCallback(int, String, long, UsageCallback, Handler)
*/
public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
UsageCallback callback) {
registerUsageCallback(networkType, subscriberId, thresholdBytes, callback,
null /* 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 live or
* {@link #unregisterUsageCallback} is called.
*
* @param networkType Type of network to monitor. Either
{@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}.
* @param subscriberId If applicable, the subscriber id of the network interface.
* <p>Starting with API level 29, the {@code subscriberId} is guarded by
* additional restrictions. Calling apps that do not meet the new
* requirements to access the {@code subscriberId} can provide a {@code
* null} value when registering for the mobile network type to receive
* notifications for all mobile networks. For additional details see {@link
* TelephonyManager#getSubscriberId()}.
* <p>Starting with API level 31, calling apps can provide a
* {@code subscriberId} with wifi network type to receive usage for
* wifi networks which is under the given subscription if applicable.
* Otherwise, pass {@code null} when querying all wifi networks.
* @param thresholdBytes Threshold in bytes to be notified on.
* @param callback The {@link UsageCallback} that the system will call when data usage
* has exceeded the specified threshold.
* @param handler to dispatch callback events through, otherwise if {@code null} it uses
* the calling thread.
*/
public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
UsageCallback callback, @Nullable Handler handler) {
NetworkTemplate template = createTemplate(networkType, subscriberId);
if (DBG) {
Log.d(TAG, "registerUsageCallback called with: {"
+ " networkType=" + networkType
+ " subscriberId=" + subscriberId
+ " thresholdBytes=" + thresholdBytes
+ " }");
}
registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
}
/**
* Unregisters callbacks on data usage.
*
* @param callback The {@link UsageCallback} used when registering.
*/
public void unregisterUsageCallback(UsageCallback callback) {
if (callback == null || callback.request == null
|| callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) {
throw new IllegalArgumentException("Invalid UsageCallback");
}
try {
mService.unregisterUsageRequest(callback.request);
} catch (RemoteException e) {
if (DBG) Log.d(TAG, "Remote exception when unregistering callback");
throw e.rethrowFromSystemServer();
}
}
/**
* 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.
*/
public abstract void onThresholdReached(int networkType, String subscriberId);
/**
* @hide used for internal bookkeeping
*/
private DataUsageRequest request;
}
/**
* Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
* to the system. To unregister, invoke {@link #unregisterNetworkStatsProvider}.
* Note that no de-duplication of statistics between providers is performed, so each provider
* must only report network traffic that is not being reported by any other provider. Also note
* that the provider cannot be re-registered after unregistering.
*
* @param tag a human readable identifier of the custom network stats provider. This is only
* used for debugging.
* @param provider the subclass of {@link NetworkStatsProvider} that needs to be
* registered to the system.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STATS_PROVIDER,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
@NonNull public void registerNetworkStatsProvider(
@NonNull String tag,
@NonNull NetworkStatsProvider provider) {
try {
if (provider.getProviderCallbackBinder() != null) {
throw new IllegalArgumentException("provider is already registered");
}
final INetworkStatsProviderCallback cbBinder =
mService.registerNetworkStatsProvider(tag, provider.getProviderBinder());
provider.setProviderCallbackBinder(cbBinder);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Unregisters an instance of {@link NetworkStatsProvider}.
*
* @param provider the subclass of {@link NetworkStatsProvider} that needs to be
* unregistered to the system.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STATS_PROVIDER,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
@NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
try {
provider.getProviderCallbackBinderOrThrow().unregister();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
final NetworkTemplate template;
switch (networkType) {
case ConnectivityManager.TYPE_MOBILE:
template = subscriberId == null
? NetworkTemplate.buildTemplateMobileWildcard()
: NetworkTemplate.buildTemplateMobileAll(subscriberId);
break;
case ConnectivityManager.TYPE_WIFI:
template = TextUtils.isEmpty(subscriberId)
? NetworkTemplate.buildTemplateWifiWildcard()
: NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL,
subscriberId);
break;
default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
+ NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
}
return template;
}
/**
* Notify {@code NetworkStatsService} about network status changed.
*
* Notifies NetworkStatsService of network state changes for data usage accounting purposes.
*
* To avoid races that attribute data usage to wrong network, such as new network with
* the same interface after SIM hot-swap, this function will not return until
* {@code NetworkStatsService} finishes its work of retrieving traffic statistics from
* all data sources.
*
* @param defaultNetworks the list of all networks that could be used by network traffic that
* does not explicitly select a network.
* @param networkStateSnapshots a list of {@link NetworkStateSnapshot}s, one for
* each network that is currently connected.
* @param activeIface the active (i.e., connected) default network interface for the calling
* uid. Used to determine on which network future calls to
* {@link android.net.TrafficStats#incrementOperationCount} applies to.
* @param underlyingNetworkInfos the list of underlying network information for all
* currently-connected VPNs.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
public void notifyNetworkStatus(
@NonNull List<Network> defaultNetworks,
@NonNull List<NetworkStateSnapshot> networkStateSnapshots,
@Nullable String activeIface,
@NonNull List<UnderlyingNetworkInfo> underlyingNetworkInfos) {
try {
Objects.requireNonNull(defaultNetworks);
Objects.requireNonNull(networkStateSnapshots);
Objects.requireNonNull(underlyingNetworkInfos);
mService.notifyNetworkStatus(defaultNetworks.toArray(new Network[0]),
networkStateSnapshots.toArray(new NetworkStateSnapshot[0]), activeIface,
underlyingNetworkInfos.toArray(new UnderlyingNetworkInfo[0]));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private static class CallbackHandler extends Handler {
private final int mNetworkType;
private final String mSubscriberId;
private UsageCallback mCallback;
CallbackHandler(Looper looper, int networkType, String subscriberId,
UsageCallback callback) {
super(looper);
mNetworkType = networkType;
mSubscriberId = subscriberId;
mCallback = callback;
}
@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;
}
}
}
private static Object getObject(Message msg, String key) {
return msg.getData().getParcelable(key);
}
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
parcelable DataUsageRequest;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package android.net;
import android.annotation.Nullable;
import android.net.NetworkTemplate;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Defines a request to register a callbacks. Used to be notified on data usage via
* {@link android.app.usage.NetworkStatsManager#registerDataUsageCallback}.
* If no {@code uid}s are set, callbacks are restricted to device-owners,
* carrier-privileged apps, or system apps.
*
* @hide
*/
public final class DataUsageRequest implements Parcelable {
public static final String PARCELABLE_KEY = "DataUsageRequest";
public static final int REQUEST_ID_UNSET = 0;
/**
* Identifies the request. {@link DataUsageRequest}s should only be constructed by
* the Framework and it is used internally to identify the request.
*/
public final int requestId;
/**
* {@link NetworkTemplate} describing the network to monitor.
*/
public final NetworkTemplate template;
/**
* Threshold in bytes to be notified on.
*/
public final long thresholdInBytes;
public DataUsageRequest(int requestId, NetworkTemplate template, long thresholdInBytes) {
this.requestId = requestId;
this.template = template;
this.thresholdInBytes = thresholdInBytes;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(requestId);
dest.writeParcelable(template, flags);
dest.writeLong(thresholdInBytes);
}
public static final @android.annotation.NonNull Creator<DataUsageRequest> CREATOR =
new Creator<DataUsageRequest>() {
@Override
public DataUsageRequest createFromParcel(Parcel in) {
int requestId = in.readInt();
NetworkTemplate template = in.readParcelable(null);
long thresholdInBytes = in.readLong();
DataUsageRequest result = new DataUsageRequest(requestId, template,
thresholdInBytes);
return result;
}
@Override
public DataUsageRequest[] newArray(int size) {
return new DataUsageRequest[size];
}
};
@Override
public String toString() {
return "DataUsageRequest [ requestId=" + requestId
+ ", networkTemplate=" + template
+ ", thresholdInBytes=" + thresholdInBytes + " ]";
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof DataUsageRequest == false) return false;
DataUsageRequest that = (DataUsageRequest) obj;
return that.requestId == this.requestId
&& Objects.equals(that.template, this.template)
&& that.thresholdInBytes == this.thresholdInBytes;
}
@Override
public int hashCode() {
return Objects.hash(requestId, template, thresholdInBytes);
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2011 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.DataUsageRequest;
import android.net.INetworkStatsSession;
import android.net.Network;
import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
/** {@hide} */
interface INetworkStatsService {
/** Start a statistics query session. */
@UnsupportedAppUsage
INetworkStatsSession openSession();
/** Start a statistics query session. If calling package is profile or device owner then it is
* granted automatic access if apiLevel is NetworkStatsManager.API_LEVEL_DPC_ALLOWED. If
* apiLevel is at least NetworkStatsManager.API_LEVEL_REQUIRES_PACKAGE_USAGE_STATS then
* PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted
* READ_NETWORK_USAGE_STATS is checked for.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage);
/** Return data layer snapshot of UID network usage. */
@UnsupportedAppUsage
NetworkStats getDataLayerSnapshotForUid(int uid);
/** Get a detailed snapshot of stats since boot for all UIDs.
*
* <p>Results will not always be limited to stats on requiredIfaces when specified: stats for
* interfaces stacked on the specified interfaces, or for interfaces on which the specified
* interfaces are stacked on, will also be included.
* @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}.
*/
NetworkStats getDetailedUidStats(in String[] requiredIfaces);
/** Return set of any ifaces associated with mobile networks since boot. */
@UnsupportedAppUsage
String[] getMobileIfaces();
/** Increment data layer count of operations performed for UID and tag. */
void incrementOperationCount(int uid, int tag, int operationCount);
/** Notify {@code NetworkStatsService} about network status changed. */
void notifyNetworkStatus(
in Network[] defaultNetworks,
in NetworkStateSnapshot[] snapshots,
in String activeIface,
in UnderlyingNetworkInfo[] underlyingNetworkInfos);
/** Force update of statistics. */
@UnsupportedAppUsage
void forceUpdate();
/** Registers a callback on data usage. */
DataUsageRequest registerUsageCallback(String callingPackage,
in DataUsageRequest request, in Messenger messenger, in IBinder binder);
/** Unregisters a callback on data usage. */
void unregisterUsageRequest(in DataUsageRequest request);
/** Get the uid stats information since boot */
long getUidStats(int uid, int type);
/** Get the iface stats information since boot */
long getIfaceStats(String iface, int type);
/** Get the total network stats information since boot */
long getTotalStats(int type);
/** Registers a network stats provider */
INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
in INetworkStatsProvider provider);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2012 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;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
/** {@hide} */
interface INetworkStatsSession {
/** Return device aggregated network layer usage summary for traffic that matches template. */
NetworkStats getDeviceSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return network layer usage summary for traffic that matches template. */
@UnsupportedAppUsage
NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return historical network layer stats for traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
/**
* Return network layer usage summary per UID for traffic that matches template.
*
* <p>The resulting {@code NetworkStats#getElapsedRealtime()} contains time delta between
* {@code start} and {@code end}.
*
* @param template - a predicate to filter netstats.
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param includeTags - includes data usage tags if true.
*/
@UnsupportedAppUsage
NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
/** Return historical network layer stats for specific UID traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
/** Return historical network layer stats for specific UID traffic that matches template. */
NetworkStatsHistory getHistoryIntervalForUid(in NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end);
/** Return array of uids that have stats and are accessible to the calling user */
int[] getRelevantUids();
@UnsupportedAppUsage
void close();
}

View File

@@ -0,0 +1,286 @@
/*
* Copyright (C) 2011 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 static android.net.ConnectivityManager.TYPE_WIFI;
import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import java.util.ArrayList;
import java.util.Objects;
/**
* Network definition that includes strong identity. Analogous to combining
* {@link NetworkCapabilities} and an IMSI.
*
* @hide
*/
public class NetworkIdentity implements Comparable<NetworkIdentity> {
private static final String TAG = "NetworkIdentity";
public static final int SUBTYPE_COMBINED = -1;
/**
* Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}.
* @hide
*/
public static final int OEM_NONE = 0x0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
* @hide
*/
public static final int OEM_PAID = 0x1;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
* @hide
*/
public static final int OEM_PRIVATE = 0x2;
final int mType;
final int mSubType;
final String mSubscriberId;
final String mNetworkId;
final boolean mRoaming;
final boolean mMetered;
final boolean mDefaultNetwork;
final int mOemManaged;
public NetworkIdentity(
int type, int subType, String subscriberId, String networkId, boolean roaming,
boolean metered, boolean defaultNetwork, int oemManaged) {
mType = type;
mSubType = subType;
mSubscriberId = subscriberId;
mNetworkId = networkId;
mRoaming = roaming;
mMetered = metered;
mDefaultNetwork = defaultNetwork;
mOemManaged = oemManaged;
}
@Override
public int hashCode() {
return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
mDefaultNetwork, mOemManaged);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
&& Objects.equals(mNetworkId, ident.mNetworkId)
&& mMetered == ident.mMetered
&& mDefaultNetwork == ident.mDefaultNetwork
&& mOemManaged == ident.mOemManaged;
}
return false;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("{");
builder.append("type=").append(mType);
builder.append(", subType=");
if (mSubType == SUBTYPE_COMBINED) {
builder.append("COMBINED");
} else {
builder.append(mSubType);
}
if (mSubscriberId != null) {
builder.append(", subscriberId=")
.append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
}
if (mRoaming) {
builder.append(", ROAMING");
}
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
return builder.append("}").toString();
}
/**
* Get the human readable representation of a bitfield representing the OEM managed state of a
* network.
*/
static String getOemManagedNames(int oemManaged) {
if (oemManaged == OEM_NONE) {
return "OEM_NONE";
}
final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
final ArrayList<String> oemManagedNames = new ArrayList<String>();
for (int position : bitPositions) {
oemManagedNames.add(nameOfOemManaged(1 << position));
}
return String.join(",", oemManagedNames);
}
private static String nameOfOemManaged(int oemManagedBit) {
switch (oemManagedBit) {
case OEM_PAID:
return "OEM_PAID";
case OEM_PRIVATE:
return "OEM_PRIVATE";
default:
return "Invalid(" + oemManagedBit + ")";
}
}
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
proto.write(NetworkIdentityProto.TYPE, mType);
// Not dumping mSubType, subtypes are no longer supported.
if (mSubscriberId != null) {
proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
proto.end(start);
}
public int getType() {
return mType;
}
public int getSubType() {
return mSubType;
}
public String getSubscriberId() {
return mSubscriberId;
}
public String getNetworkId() {
return mNetworkId;
}
public boolean getRoaming() {
return mRoaming;
}
public boolean getMetered() {
return mMetered;
}
public boolean getDefaultNetwork() {
return mDefaultNetwork;
}
public int getOemManaged() {
return mOemManaged;
}
/**
* Build a {@link NetworkIdentity} from the given {@link NetworkStateSnapshot} and
* {@code subType}, assuming that any mobile networks are using the current IMSI.
* The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_*
* constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
*/
public static NetworkIdentity buildNetworkIdentity(Context context,
NetworkStateSnapshot snapshot, boolean defaultNetwork, @NetworkType int subType) {
final int legacyType = snapshot.getLegacyType();
final String subscriberId = snapshot.getSubscriberId();
String networkId = null;
boolean roaming = !snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
boolean metered = !(snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
|| snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
if (legacyType == TYPE_WIFI) {
networkId = snapshot.getNetworkCapabilities().getSsid();
if (networkId == null) {
final WifiManager wifi = context.getSystemService(WifiManager.class);
final WifiInfo info = wifi.getConnectionInfo();
networkId = info != null ? info.getSSID() : null;
}
}
return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
defaultNetwork, oemManaged);
}
/**
* Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}.
* @hide
*/
public static int getOemBitfield(NetworkCapabilities nc) {
int oemManaged = OEM_NONE;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
oemManaged |= OEM_PAID;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) {
oemManaged |= OEM_PRIVATE;
}
return oemManaged;
}
@Override
public int compareTo(NetworkIdentity another) {
int res = Integer.compare(mType, another.mType);
if (res == 0) {
res = Integer.compare(mSubType, another.mSubType);
}
if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
res = mSubscriberId.compareTo(another.mSubscriberId);
}
if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
res = mNetworkId.compareTo(another.mNetworkId);
}
if (res == 0) {
res = Boolean.compare(mRoaming, another.mRoaming);
}
if (res == 0) {
res = Boolean.compare(mMetered, another.mMetered);
}
if (res == 0) {
res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
}
if (res == 0) {
res = Integer.compare(mOemManaged, another.mOemManaged);
}
return res;
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2021 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 static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
/**
* Snapshot of network state.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkStateSnapshot implements Parcelable {
/** The network associated with this snapshot. */
@NonNull
private final Network mNetwork;
/** The {@link NetworkCapabilities} of the network associated with this snapshot. */
@NonNull
private final NetworkCapabilities mNetworkCapabilities;
/** The {@link LinkProperties} of the network associated with this snapshot. */
@NonNull
private final LinkProperties mLinkProperties;
/**
* The Subscriber Id of the network associated with this snapshot. See
* {@link android.telephony.TelephonyManager#getSubscriberId()}.
*/
@Nullable
private final String mSubscriberId;
/**
* The legacy type of the network associated with this snapshot. See
* {@code ConnectivityManager#TYPE_*}.
*/
private final int mLegacyType;
public NetworkStateSnapshot(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
@Nullable String subscriberId, int legacyType) {
mNetwork = Objects.requireNonNull(network);
mNetworkCapabilities = Objects.requireNonNull(networkCapabilities);
mLinkProperties = Objects.requireNonNull(linkProperties);
mSubscriberId = subscriberId;
mLegacyType = legacyType;
}
/** @hide */
public NetworkStateSnapshot(@NonNull Parcel in) {
mNetwork = in.readParcelable(null);
mNetworkCapabilities = in.readParcelable(null);
mLinkProperties = in.readParcelable(null);
mSubscriberId = in.readString();
mLegacyType = in.readInt();
}
/** Get the network associated with this snapshot */
@NonNull
public Network getNetwork() {
return mNetwork;
}
/** Get {@link NetworkCapabilities} of the network associated with this snapshot. */
@NonNull
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
}
/** Get the {@link LinkProperties} of the network associated with this snapshot. */
@NonNull
public LinkProperties getLinkProperties() {
return mLinkProperties;
}
/** Get the Subscriber Id of the network associated with this snapshot. */
@Nullable
public String getSubscriberId() {
return mSubscriberId;
}
/**
* Get the legacy type of the network associated with this snapshot.
* @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
*/
public int getLegacyType() {
return mLegacyType;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mNetwork, flags);
out.writeParcelable(mNetworkCapabilities, flags);
out.writeParcelable(mLinkProperties, flags);
out.writeString(mSubscriberId);
out.writeInt(mLegacyType);
}
@NonNull
public static final Creator<NetworkStateSnapshot> CREATOR =
new Creator<NetworkStateSnapshot>() {
@NonNull
@Override
public NetworkStateSnapshot createFromParcel(@NonNull Parcel in) {
return new NetworkStateSnapshot(in);
}
@NonNull
@Override
public NetworkStateSnapshot[] newArray(int size) {
return new NetworkStateSnapshot[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NetworkStateSnapshot)) return false;
NetworkStateSnapshot that = (NetworkStateSnapshot) o;
return mLegacyType == that.mLegacyType
&& Objects.equals(mNetwork, that.mNetwork)
&& Objects.equals(mNetworkCapabilities, that.mNetworkCapabilities)
&& Objects.equals(mLinkProperties, that.mLinkProperties)
&& Objects.equals(mSubscriberId, that.mSubscriberId);
}
@Override
public int hashCode() {
return Objects.hash(mNetwork,
mNetworkCapabilities, mLinkProperties, mSubscriberId, mLegacyType);
}
@Override
public String toString() {
return "NetworkStateSnapshot{"
+ "network=" + mNetwork
+ ", networkCapabilities=" + mNetworkCapabilities
+ ", linkProperties=" + mLinkProperties
+ ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(mSubscriberId) + '\''
+ ", legacyType=" + mLegacyType
+ '}';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2011, 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;
parcelable NetworkStatsHistory;

View File

@@ -0,0 +1,881 @@
/*
* Copyright (C) 2011 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 static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray;
import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLongArray;
import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
import static com.android.internal.util.ArrayUtils.total;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.NetworkStatsHistoryBucketProto;
import android.service.NetworkStatsHistoryProto;
import android.util.MathUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
import libcore.util.EmptyArray;
import java.io.CharArrayWriter;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
import java.util.Arrays;
import java.util.Random;
/**
* Collection of historical network statistics, recorded into equally-sized
* "buckets" in time. Internally it stores data in {@code long} series for more
* efficient persistence.
* <p>
* Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for
* {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is
* sorted at all times.
*
* @hide
*/
public class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_PACKETS = 2;
private static final int VERSION_ADD_ACTIVE = 3;
public static final int FIELD_ACTIVE_TIME = 0x01;
public static final int FIELD_RX_BYTES = 0x02;
public static final int FIELD_RX_PACKETS = 0x04;
public static final int FIELD_TX_BYTES = 0x08;
public static final int FIELD_TX_PACKETS = 0x10;
public static final int FIELD_OPERATIONS = 0x20;
public static final int FIELD_ALL = 0xFFFFFFFF;
private long bucketDuration;
private int bucketCount;
private long[] bucketStart;
private long[] activeTime;
private long[] rxBytes;
private long[] rxPackets;
private long[] txBytes;
private long[] txPackets;
private long[] operations;
private long totalBytes;
public static class Entry {
public static final long UNKNOWN = -1;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketDuration;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketStart;
public long activeTime;
@UnsupportedAppUsage
public long rxBytes;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long rxPackets;
@UnsupportedAppUsage
public long txBytes;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long txPackets;
public long operations;
}
@UnsupportedAppUsage
public NetworkStatsHistory(long bucketDuration) {
this(bucketDuration, 10, FIELD_ALL);
}
public NetworkStatsHistory(long bucketDuration, int initialSize) {
this(bucketDuration, initialSize, FIELD_ALL);
}
public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
this.bucketDuration = bucketDuration;
bucketStart = new long[initialSize];
if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize];
if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
bucketCount = 0;
totalBytes = 0;
}
public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
recordEntireHistory(existing);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public NetworkStatsHistory(Parcel in) {
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
activeTime = readLongArray(in);
rxBytes = readLongArray(in);
rxPackets = readLongArray(in);
txBytes = readLongArray(in);
txPackets = readLongArray(in);
operations = readLongArray(in);
bucketCount = bucketStart.length;
totalBytes = in.readLong();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, activeTime, bucketCount);
writeLongArray(out, rxBytes, bucketCount);
writeLongArray(out, rxPackets, bucketCount);
writeLongArray(out, txBytes, bucketCount);
writeLongArray(out, txPackets, bucketCount);
writeLongArray(out, operations, bucketCount);
out.writeLong(totalBytes);
}
public NetworkStatsHistory(DataInput in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT: {
bucketDuration = in.readLong();
bucketStart = readFullLongArray(in);
rxBytes = readFullLongArray(in);
rxPackets = new long[bucketStart.length];
txBytes = readFullLongArray(in);
txPackets = new long[bucketStart.length];
operations = new long[bucketStart.length];
bucketCount = bucketStart.length;
totalBytes = total(rxBytes) + total(txBytes);
break;
}
case VERSION_ADD_PACKETS:
case VERSION_ADD_ACTIVE: {
bucketDuration = in.readLong();
bucketStart = readVarLongArray(in);
activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in)
: new long[bucketStart.length];
rxBytes = readVarLongArray(in);
rxPackets = readVarLongArray(in);
txBytes = readVarLongArray(in);
txPackets = readVarLongArray(in);
operations = readVarLongArray(in);
bucketCount = bucketStart.length;
totalBytes = total(rxBytes) + total(txBytes);
break;
}
default: {
throw new ProtocolException("unexpected version: " + version);
}
}
if (bucketStart.length != bucketCount || rxBytes.length != bucketCount
|| rxPackets.length != bucketCount || txBytes.length != bucketCount
|| txPackets.length != bucketCount || operations.length != bucketCount) {
throw new ProtocolException("Mismatched history lengths");
}
}
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_ACTIVE);
out.writeLong(bucketDuration);
writeVarLongArray(out, bucketStart, bucketCount);
writeVarLongArray(out, activeTime, bucketCount);
writeVarLongArray(out, rxBytes, bucketCount);
writeVarLongArray(out, rxPackets, bucketCount);
writeVarLongArray(out, txBytes, bucketCount);
writeVarLongArray(out, txPackets, bucketCount);
writeVarLongArray(out, operations, bucketCount);
}
@Override
public int describeContents() {
return 0;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int size() {
return bucketCount;
}
public long getBucketDuration() {
return bucketDuration;
}
@UnsupportedAppUsage
public long getStart() {
if (bucketCount > 0) {
return bucketStart[0];
} else {
return Long.MAX_VALUE;
}
}
@UnsupportedAppUsage
public long getEnd() {
if (bucketCount > 0) {
return bucketStart[bucketCount - 1] + bucketDuration;
} else {
return Long.MIN_VALUE;
}
}
/**
* Return total bytes represented by this history.
*/
public long getTotalBytes() {
return totalBytes;
}
/**
* Return index of bucket that contains or is immediately before the
* requested time.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getIndexBefore(long time) {
int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
if (index < 0) {
index = (~index) - 1;
} else {
index -= 1;
}
return MathUtils.constrain(index, 0, bucketCount - 1);
}
/**
* Return index of bucket that contains or is immediately after the
* requested time.
*/
public int getIndexAfter(long time) {
int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
if (index < 0) {
index = ~index;
} else {
index += 1;
}
return MathUtils.constrain(index, 0, bucketCount - 1);
}
/**
* Return specific stats entry.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Entry getValues(int i, Entry recycle) {
final Entry entry = recycle != null ? recycle : new Entry();
entry.bucketStart = bucketStart[i];
entry.bucketDuration = bucketDuration;
entry.activeTime = getLong(activeTime, i, UNKNOWN);
entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
entry.txBytes = getLong(txBytes, i, UNKNOWN);
entry.txPackets = getLong(txPackets, i, UNKNOWN);
entry.operations = getLong(operations, i, UNKNOWN);
return entry;
}
public void setValues(int i, Entry entry) {
// Unwind old values
if (rxBytes != null) totalBytes -= rxBytes[i];
if (txBytes != null) totalBytes -= txBytes[i];
bucketStart[i] = entry.bucketStart;
setLong(activeTime, i, entry.activeTime);
setLong(rxBytes, i, entry.rxBytes);
setLong(rxPackets, i, entry.rxPackets);
setLong(txBytes, i, entry.txBytes);
setLong(txPackets, i, entry.txPackets);
setLong(operations, i, entry.operations);
// Apply new values
if (rxBytes != null) totalBytes += rxBytes[i];
if (txBytes != null) totalBytes += txBytes[i];
}
/**
* Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed.
*/
@Deprecated
public void recordData(long start, long end, long rxBytes, long txBytes) {
recordData(start, end, new NetworkStats.Entry(
IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
}
/**
* Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed.
*/
public void recordData(long start, long end, NetworkStats.Entry entry) {
long rxBytes = entry.rxBytes;
long rxPackets = entry.rxPackets;
long txBytes = entry.txBytes;
long txPackets = entry.txPackets;
long operations = entry.operations;
if (entry.isNegative()) {
throw new IllegalArgumentException("tried recording negative data");
}
if (entry.isEmpty()) {
return;
}
// create any buckets needed by this range
ensureBuckets(start, end);
// distribute data usage into buckets
long duration = end - start;
final int startIndex = getIndexAfter(end);
for (int i = startIndex; i >= 0; i--) {
final long curStart = bucketStart[i];
final long curEnd = curStart + bucketDuration;
// bucket is older than record; we're finished
if (curEnd < start) break;
// bucket is newer than record; keep looking
if (curStart > end) continue;
final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
if (overlap <= 0) continue;
// integer math each time is faster than floating point
final long fracRxBytes = multiplySafeByRational(rxBytes, overlap, duration);
final long fracRxPackets = multiplySafeByRational(rxPackets, overlap, duration);
final long fracTxBytes = multiplySafeByRational(txBytes, overlap, duration);
final long fracTxPackets = multiplySafeByRational(txPackets, overlap, duration);
final long fracOperations = multiplySafeByRational(operations, overlap, duration);
addLong(activeTime, i, overlap);
addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
addLong(this.operations, i, fracOperations); operations -= fracOperations;
duration -= overlap;
}
totalBytes += entry.rxBytes + entry.txBytes;
}
/**
* Record an entire {@link NetworkStatsHistory} into this history. Usually
* for combining together stats for external reporting.
*/
@UnsupportedAppUsage
public void recordEntireHistory(NetworkStatsHistory input) {
recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE);
}
/**
* Record given {@link NetworkStatsHistory} into this history, copying only
* buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets.
*/
public void recordHistory(NetworkStatsHistory input, long start, long end) {
final NetworkStats.Entry entry = new NetworkStats.Entry(
IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
for (int i = 0; i < input.bucketCount; i++) {
final long bucketStart = input.bucketStart[i];
final long bucketEnd = bucketStart + input.bucketDuration;
// skip when bucket is outside requested range
if (bucketStart < start || bucketEnd > end) continue;
entry.rxBytes = getLong(input.rxBytes, i, 0L);
entry.rxPackets = getLong(input.rxPackets, i, 0L);
entry.txBytes = getLong(input.txBytes, i, 0L);
entry.txPackets = getLong(input.txPackets, i, 0L);
entry.operations = getLong(input.operations, i, 0L);
recordData(bucketStart, bucketEnd, entry);
}
}
/**
* Ensure that buckets exist for given time range, creating as needed.
*/
private void ensureBuckets(long start, long end) {
// normalize incoming range to bucket boundaries
start -= start % bucketDuration;
end += (bucketDuration - (end % bucketDuration)) % bucketDuration;
for (long now = start; now < end; now += bucketDuration) {
// try finding existing bucket
final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now);
if (index < 0) {
// bucket missing, create and insert
insertBucket(~index, now);
}
}
}
/**
* Insert new bucket at requested index and starting time.
*/
private void insertBucket(int index, long start) {
// create more buckets when needed
if (bucketCount >= bucketStart.length) {
final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength);
if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength);
if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength);
if (operations != null) operations = Arrays.copyOf(operations, newLength);
}
// create gap when inserting bucket in middle
if (index < bucketCount) {
final int dstPos = index + 1;
final int length = bucketCount - index;
System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length);
if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length);
if (operations != null) System.arraycopy(operations, index, operations, dstPos, length);
}
bucketStart[index] = start;
setLong(activeTime, index, 0L);
setLong(rxBytes, index, 0L);
setLong(rxPackets, index, 0L);
setLong(txBytes, index, 0L);
setLong(txPackets, index, 0L);
setLong(operations, index, 0L);
bucketCount++;
}
/**
* Clear all data stored in this object.
*/
public void clear() {
bucketStart = EmptyArray.LONG;
if (activeTime != null) activeTime = EmptyArray.LONG;
if (rxBytes != null) rxBytes = EmptyArray.LONG;
if (rxPackets != null) rxPackets = EmptyArray.LONG;
if (txBytes != null) txBytes = EmptyArray.LONG;
if (txPackets != null) txPackets = EmptyArray.LONG;
if (operations != null) operations = EmptyArray.LONG;
bucketCount = 0;
totalBytes = 0;
}
/**
* Remove buckets older than requested cutoff.
*/
@Deprecated
public void removeBucketsBefore(long cutoff) {
int i;
for (i = 0; i < bucketCount; i++) {
final long curStart = bucketStart[i];
final long curEnd = curStart + bucketDuration;
// cutoff happens before or during this bucket; everything before
// this bucket should be removed.
if (curEnd > cutoff) break;
}
if (i > 0) {
final int length = bucketStart.length;
bucketStart = Arrays.copyOfRange(bucketStart, i, length);
if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length);
if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
bucketCount -= i;
// TODO: subtract removed values from totalBytes
}
}
/**
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
*
* <p>If the active bucket is not completed yet, it returns the proportional value of it
* based on its duration and the {@code end} param.
*
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param recycle - entry instance for performance, could be null.
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, Entry recycle) {
return getValues(start, end, Long.MAX_VALUE, recycle);
}
/**
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
*
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param now - current timestamp in milliseconds since the epoch (wall clock).
* @param recycle - entry instance for performance, could be null.
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, long now, Entry recycle) {
final Entry entry = recycle != null ? recycle : new Entry();
entry.bucketDuration = end - start;
entry.bucketStart = start;
entry.activeTime = activeTime != null ? 0 : UNKNOWN;
entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
entry.txBytes = txBytes != null ? 0 : UNKNOWN;
entry.txPackets = txPackets != null ? 0 : UNKNOWN;
entry.operations = operations != null ? 0 : UNKNOWN;
final int startIndex = getIndexAfter(end);
for (int i = startIndex; i >= 0; i--) {
final long curStart = bucketStart[i];
long curEnd = curStart + bucketDuration;
// bucket is older than request; we're finished
if (curEnd <= start) break;
// bucket is newer than request; keep looking
if (curStart >= end) continue;
// the active bucket is shorter then a normal completed bucket
if (curEnd > now) curEnd = now;
// usually this is simply bucketDuration
final long bucketSpan = curEnd - curStart;
// prevent division by zero
if (bucketSpan <= 0) continue;
final long overlapEnd = curEnd < end ? curEnd : end;
final long overlapStart = curStart > start ? curStart : start;
final long overlap = overlapEnd - overlapStart;
if (overlap <= 0) continue;
// integer math each time is faster than floating point
if (activeTime != null) {
entry.activeTime += multiplySafeByRational(activeTime[i], overlap, bucketSpan);
}
if (rxBytes != null) {
entry.rxBytes += multiplySafeByRational(rxBytes[i], overlap, bucketSpan);
}
if (rxPackets != null) {
entry.rxPackets += multiplySafeByRational(rxPackets[i], overlap, bucketSpan);
}
if (txBytes != null) {
entry.txBytes += multiplySafeByRational(txBytes[i], overlap, bucketSpan);
}
if (txPackets != null) {
entry.txPackets += multiplySafeByRational(txPackets[i], overlap, bucketSpan);
}
if (operations != null) {
entry.operations += multiplySafeByRational(operations[i], overlap, bucketSpan);
}
}
return entry;
}
/**
* @deprecated only for temporary testing
*/
@Deprecated
public void generateRandom(long start, long end, long bytes) {
final Random r = new Random();
final float fractionRx = r.nextFloat();
final long rxBytes = (long) (bytes * fractionRx);
final long txBytes = (long) (bytes * (1 - fractionRx));
final long rxPackets = rxBytes / 1024;
final long txPackets = txBytes / 1024;
final long operations = rxBytes / 2048;
generateRandom(start, end, rxBytes, rxPackets, txBytes, txPackets, operations, r);
}
/**
* @deprecated only for temporary testing
*/
@Deprecated
public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
long txPackets, long operations, Random r) {
ensureBuckets(start, end);
final NetworkStats.Entry entry = new NetworkStats.Entry(
IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
|| operations > 32) {
final long curStart = randomLong(r, start, end);
final long curEnd = curStart + randomLong(r, 0, (end - curStart) / 2);
entry.rxBytes = randomLong(r, 0, rxBytes);
entry.rxPackets = randomLong(r, 0, rxPackets);
entry.txBytes = randomLong(r, 0, txBytes);
entry.txPackets = randomLong(r, 0, txPackets);
entry.operations = randomLong(r, 0, operations);
rxBytes -= entry.rxBytes;
rxPackets -= entry.rxPackets;
txBytes -= entry.txBytes;
txPackets -= entry.txPackets;
operations -= entry.operations;
recordData(curStart, curEnd, entry);
}
}
public static long randomLong(Random r, long start, long end) {
return (long) (start + (r.nextFloat() * (end - start)));
}
/**
* Quickly determine if this history intersects with given window.
*/
public boolean intersects(long start, long end) {
final long dataStart = getStart();
final long dataEnd = getEnd();
if (start >= dataStart && start <= dataEnd) return true;
if (end >= dataStart && end <= dataEnd) return true;
if (dataStart >= start && dataStart <= end) return true;
if (dataEnd >= start && dataEnd <= end) return true;
return false;
}
public void dump(IndentingPrintWriter pw, boolean fullHistory) {
pw.print("NetworkStatsHistory: bucketDuration=");
pw.println(bucketDuration / SECOND_IN_MILLIS);
pw.increaseIndent();
final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
if (start > 0) {
pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
}
for (int i = start; i < bucketCount; i++) {
pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
pw.println();
}
pw.decreaseIndent();
}
public void dumpCheckin(PrintWriter pw) {
pw.print("d,");
pw.print(bucketDuration / SECOND_IN_MILLIS);
pw.println();
for (int i = 0; i < bucketCount; i++) {
pw.print("b,");
pw.print(bucketStart[i] / SECOND_IN_MILLIS); pw.print(',');
if (rxBytes != null) { pw.print(rxBytes[i]); } else { pw.print("*"); } pw.print(',');
if (rxPackets != null) { pw.print(rxPackets[i]); } else { pw.print("*"); } pw.print(',');
if (txBytes != null) { pw.print(txBytes[i]); } else { pw.print("*"); } pw.print(',');
if (txPackets != null) { pw.print(txPackets[i]); } else { pw.print("*"); } pw.print(',');
if (operations != null) { pw.print(operations[i]); } else { pw.print("*"); }
pw.println();
}
}
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
for (int i = 0; i < bucketCount; i++) {
final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
proto.end(startBucket);
}
proto.end(start);
}
private static void dumpDebug(ProtoOutputStream proto, long tag, long[] array, int index) {
if (array != null) {
proto.write(tag, array[index]);
}
}
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
dump(new IndentingPrintWriter(writer, " "), false);
return writer.toString();
}
@UnsupportedAppUsage
public static final @android.annotation.NonNull Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() {
@Override
public NetworkStatsHistory createFromParcel(Parcel in) {
return new NetworkStatsHistory(in);
}
@Override
public NetworkStatsHistory[] newArray(int size) {
return new NetworkStatsHistory[size];
}
};
private static long getLong(long[] array, int i, long value) {
return array != null ? array[i] : value;
}
private static void setLong(long[] array, int i, long value) {
if (array != null) array[i] = value;
}
private static void addLong(long[] array, int i, long value) {
if (array != null) array[i] += value;
}
public int estimateResizeBuckets(long newBucketDuration) {
return (int) (size() * getBucketDuration() / newBucketDuration);
}
/**
* Utility methods for interacting with {@link DataInputStream} and
* {@link DataOutputStream}, mostly dealing with writing partial arrays.
*/
public static class DataStreamUtils {
@Deprecated
public static long[] readFullLongArray(DataInput in) throws IOException {
final int size = in.readInt();
if (size < 0) throw new ProtocolException("negative array size");
final long[] values = new long[size];
for (int i = 0; i < values.length; i++) {
values[i] = in.readLong();
}
return values;
}
/**
* Read variable-length {@link Long} using protobuf-style approach.
*/
public static long readVarLong(DataInput in) throws IOException {
int shift = 0;
long result = 0;
while (shift < 64) {
byte b = in.readByte();
result |= (long) (b & 0x7F) << shift;
if ((b & 0x80) == 0)
return result;
shift += 7;
}
throw new ProtocolException("malformed long");
}
/**
* Write variable-length {@link Long} using protobuf-style approach.
*/
public static void writeVarLong(DataOutput out, long value) throws IOException {
while (true) {
if ((value & ~0x7FL) == 0) {
out.writeByte((int) value);
return;
} else {
out.writeByte(((int) value & 0x7F) | 0x80);
value >>>= 7;
}
}
}
public static long[] readVarLongArray(DataInput in) throws IOException {
final int size = in.readInt();
if (size == -1) return null;
if (size < 0) throw new ProtocolException("negative array size");
final long[] values = new long[size];
for (int i = 0; i < values.length; i++) {
values[i] = readVarLong(in);
}
return values;
}
public static void writeVarLongArray(DataOutput out, long[] values, int size)
throws IOException {
if (values == null) {
out.writeInt(-1);
return;
}
if (size > values.length) {
throw new IllegalArgumentException("size larger than length");
}
out.writeInt(size);
for (int i = 0; i < size; i++) {
writeVarLong(out, values[i]);
}
}
}
/**
* Utility methods for interacting with {@link Parcel} structures, mostly
* dealing with writing partial arrays.
*/
public static class ParcelUtils {
public static long[] readLongArray(Parcel in) {
final int size = in.readInt();
if (size == -1) return null;
final long[] values = new long[size];
for (int i = 0; i < values.length; i++) {
values[i] = in.readLong();
}
return values;
}
public static void writeLongArray(Parcel out, long[] values, int size) {
if (values == null) {
out.writeInt(-1);
return;
}
if (size > values.length) {
throw new IllegalArgumentException("size larger than length");
}
out.writeInt(size);
for (int i = 0; i < size; i++) {
out.writeLong(values[i]);
}
}
}
}

View File

@@ -0,0 +1,906 @@
/*
* Copyright (C) 2011 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 static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
import static android.net.wifi.WifiInfo.sanitizeSsid;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Annotation.NetworkType;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.BackupUtils;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
/**
* Predicate used to match {@link NetworkIdentity}, usually when collecting
* statistics. (It should probably have been named {@code NetworkPredicate}.)
*
* @hide
*/
public class NetworkTemplate implements Parcelable {
private static final String TAG = "NetworkTemplate";
/**
* Initial Version of the backup serializer.
*/
public static final int BACKUP_VERSION_1_INIT = 1;
/**
* Version of the backup serializer that added carrier template support.
*/
public static final int BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2;
/**
* Current Version of the Backup Serializer.
*/
private static final int BACKUP_VERSION = BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE;
public static final int MATCH_MOBILE = 1;
public static final int MATCH_WIFI = 4;
public static final int MATCH_ETHERNET = 5;
public static final int MATCH_MOBILE_WILDCARD = 6;
public static final int MATCH_WIFI_WILDCARD = 7;
public static final int MATCH_BLUETOOTH = 8;
public static final int MATCH_PROXY = 9;
public static final int MATCH_CARRIER = 10;
/**
* Value of the match rule of the subscriberId to match networks with specific subscriberId.
*/
public static final int SUBSCRIBER_ID_MATCH_RULE_EXACT = 0;
/**
* Value of the match rule of the subscriberId to match networks with any subscriberId which
* includes null and non-null.
*/
public static final int SUBSCRIBER_ID_MATCH_RULE_ALL = 1;
/**
* Wi-Fi Network ID is never supposed to be null (if it is, it is a bug that
* should be fixed), so it's not possible to want to match null vs
* non-null. Therefore it's fine to use null as a sentinel for Network ID.
*/
public static final String WIFI_NETWORKID_ALL = null;
/**
* Include all network types when filtering. This is meant to merge in with the
* {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync.
*
* @hide
*/
public static final int NETWORK_TYPE_ALL = -1;
/**
* Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is
* still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along
* with NR state as connected. This should not be overlapped with any of the
* {@code TelephonyManager.NETWORK_TYPE_*} constants.
*
* @hide
*/
public static final int NETWORK_TYPE_5G_NSA = -2;
/**
* Value to match both OEM managed and unmanaged networks (all networks).
* @hide
*/
public static final int OEM_MANAGED_ALL = -1;
/**
* Value to match networks which are not OEM managed.
* @hide
*/
public static final int OEM_MANAGED_NO = OEM_NONE;
/**
* Value to match any OEM managed network.
* @hide
*/
public static final int OEM_MANAGED_YES = -2;
private static boolean isKnownMatchRule(final int rule) {
switch (rule) {
case MATCH_MOBILE:
case MATCH_WIFI:
case MATCH_ETHERNET:
case MATCH_MOBILE_WILDCARD:
case MATCH_WIFI_WILDCARD:
case MATCH_BLUETOOTH:
case MATCH_PROXY:
case MATCH_CARRIER:
return true;
default:
return false;
}
}
/**
* Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
* the given IMSI.
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
return new NetworkTemplate(MATCH_MOBILE, subscriberId, null);
}
/**
* Template to match cellular networks with the given IMSI, {@code ratType} and
* {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
* filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
*/
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
@NetworkType int ratType, int metered) {
if (TextUtils.isEmpty(subscriberId)) {
return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
/**
* Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks,
* regardless of IMSI.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static NetworkTemplate buildTemplateMobileWildcard() {
return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);
}
/**
* Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
* regardless of SSID.
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateWifiWildcard() {
// TODO: Consider replace this with MATCH_WIFI with NETWORK_ID_ALL
// and SUBSCRIBER_ID_MATCH_RULE_ALL.
return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
}
@Deprecated
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateWifi() {
return buildTemplateWifiWildcard();
}
/**
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given SSID.
*/
public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
Objects.requireNonNull(networkId);
return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
new String[] { null } /* matchSubscriberIds */,
networkId, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_ALL);
}
/**
* Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
* and IMSI.
*
* Call with {@link #WIFI_NETWORKID_ALL} for {@code networkId} to get result regardless of SSID.
*/
public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
@Nullable String subscriberId) {
return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
networkId, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
/**
* Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
* networks together.
*/
@UnsupportedAppUsage
public static NetworkTemplate buildTemplateEthernet() {
return new NetworkTemplate(MATCH_ETHERNET, null, null);
}
/**
* Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style
* networks together.
*/
public static NetworkTemplate buildTemplateBluetooth() {
return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
}
/**
* Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
* networks together.
*/
public static NetworkTemplate buildTemplateProxy() {
return new NetworkTemplate(MATCH_PROXY, null, null);
}
/**
* Template to match all metered carrier networks with the given IMSI.
*/
public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
Objects.requireNonNull(subscriberId);
return new NetworkTemplate(MATCH_CARRIER, subscriberId,
new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
private final int mMatchRule;
private final String mSubscriberId;
/**
* Ugh, templates are designed to target a single subscriber, but we might
* need to match several "merged" subscribers. These are the subscribers
* that should be considered to match this template.
* <p>
* Since the merge set is dynamic, it should <em>not</em> be persisted or
* used for determining equality.
*/
private final String[] mMatchSubscriberIds;
private final String mNetworkId;
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
private final int mMetered;
private final int mRoaming;
private final int mDefaultNetwork;
private final int mSubType;
/**
* The subscriber Id match rule defines how the template should match networks with
* specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail.
*/
private final int mSubscriberIdMatchRule;
// Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}.
private final int mOemManaged;
private void checkValidSubscriberIdMatchRule() {
switch (mMatchRule) {
case MATCH_MOBILE:
case MATCH_CARRIER:
// MOBILE and CARRIER templates must always specify a subscriber ID.
if (mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL) {
throw new IllegalArgumentException("Invalid SubscriberIdMatchRule"
+ "on match rule: " + getMatchRuleName(mMatchRule));
}
return;
default:
return;
}
}
// TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
@UnsupportedAppUsage
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
}
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
// Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
// to metered networks. It is now possible to match mobile with any meteredness, but
// in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
//constructor passes METERED_YES for these types.
this(matchRule, subscriberId, matchSubscriberIds, networkId,
(matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
: METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
// TODO: Remove it after updating all of the caller.
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId, int metered, int roaming, int defaultNetwork, int subType,
int oemManaged) {
this(matchRule, subscriberId, matchSubscriberIds, networkId, metered, roaming,
defaultNetwork, subType, oemManaged, SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId, int metered, int roaming, int defaultNetwork, int subType,
int oemManaged, int subscriberIdMatchRule) {
mMatchRule = matchRule;
mSubscriberId = subscriberId;
// TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
// mSubscriberId is null
mMatchSubscriberIds = matchSubscriberIds;
mNetworkId = networkId;
mMetered = metered;
mRoaming = roaming;
mDefaultNetwork = defaultNetwork;
mSubType = subType;
mOemManaged = oemManaged;
mSubscriberIdMatchRule = subscriberIdMatchRule;
checkValidSubscriberIdMatchRule();
if (!isKnownMatchRule(matchRule)) {
throw new IllegalArgumentException("Unknown network template rule " + matchRule
+ " will not match any identity.");
}
}
private NetworkTemplate(Parcel in) {
mMatchRule = in.readInt();
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
mNetworkId = in.readString();
mMetered = in.readInt();
mRoaming = in.readInt();
mDefaultNetwork = in.readInt();
mSubType = in.readInt();
mOemManaged = in.readInt();
mSubscriberIdMatchRule = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mMatchRule);
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
dest.writeString(mNetworkId);
dest.writeInt(mMetered);
dest.writeInt(mRoaming);
dest.writeInt(mDefaultNetwork);
dest.writeInt(mSubType);
dest.writeInt(mOemManaged);
dest.writeInt(mSubscriberIdMatchRule);
}
@Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("NetworkTemplate: ");
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
if (mSubscriberId != null) {
builder.append(", subscriberId=").append(
NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mMatchSubscriberIds != null) {
builder.append(", matchSubscriberIds=").append(
Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
}
if (mMetered != METERED_ALL) {
builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
}
if (mRoaming != ROAMING_ALL) {
builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
}
if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
mDefaultNetwork));
}
if (mSubType != NETWORK_TYPE_ALL) {
builder.append(", subType=").append(mSubType);
}
if (mOemManaged != OEM_MANAGED_ALL) {
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
}
builder.append(", subscriberIdMatchRule=")
.append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
return builder.toString();
}
@Override
public int hashCode() {
return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkTemplate) {
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
&& Objects.equals(mSubscriberId, other.mSubscriberId)
&& Objects.equals(mNetworkId, other.mNetworkId)
&& mMetered == other.mMetered
&& mRoaming == other.mRoaming
&& mDefaultNetwork == other.mDefaultNetwork
&& mSubType == other.mSubType
&& mOemManaged == other.mOemManaged
&& mSubscriberIdMatchRule == other.mSubscriberIdMatchRule;
}
return false;
}
private String subscriberIdMatchRuleToString(int rule) {
switch (rule) {
case SUBSCRIBER_ID_MATCH_RULE_EXACT:
return "EXACT_MATCH";
case SUBSCRIBER_ID_MATCH_RULE_ALL:
return "ALL";
default:
return "Unknown rule " + rule;
}
}
public boolean isMatchRuleMobile() {
switch (mMatchRule) {
case MATCH_MOBILE:
case MATCH_MOBILE_WILDCARD:
return true;
default:
return false;
}
}
public boolean isPersistable() {
switch (mMatchRule) {
case MATCH_MOBILE_WILDCARD:
case MATCH_WIFI_WILDCARD:
return false;
case MATCH_CARRIER:
return mSubscriberId != null;
case MATCH_WIFI:
if (Objects.equals(mNetworkId, WIFI_NETWORKID_ALL)
&& mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL) {
return false;
}
return true;
default:
return true;
}
}
@UnsupportedAppUsage
public int getMatchRule() {
return mMatchRule;
}
@UnsupportedAppUsage
public String getSubscriberId() {
return mSubscriberId;
}
public String getNetworkId() {
return mNetworkId;
}
public int getSubscriberIdMatchRule() {
return mSubscriberIdMatchRule;
}
public int getMeteredness() {
return mMetered;
}
/**
* Test if given {@link NetworkIdentity} matches this template.
*/
public boolean matches(NetworkIdentity ident) {
if (!matchesMetered(ident)) return false;
if (!matchesRoaming(ident)) return false;
if (!matchesDefaultNetwork(ident)) return false;
if (!matchesOemNetwork(ident)) return false;
switch (mMatchRule) {
case MATCH_MOBILE:
return matchesMobile(ident);
case MATCH_WIFI:
return matchesWifi(ident);
case MATCH_ETHERNET:
return matchesEthernet(ident);
case MATCH_MOBILE_WILDCARD:
return matchesMobileWildcard(ident);
case MATCH_WIFI_WILDCARD:
return matchesWifiWildcard(ident);
case MATCH_BLUETOOTH:
return matchesBluetooth(ident);
case MATCH_PROXY:
return matchesProxy(ident);
case MATCH_CARRIER:
return matchesCarrier(ident);
default:
// We have no idea what kind of network template we are, so we
// just claim not to match anything.
return false;
}
}
private boolean matchesMetered(NetworkIdentity ident) {
return (mMetered == METERED_ALL)
|| (mMetered == METERED_YES && ident.mMetered)
|| (mMetered == METERED_NO && !ident.mMetered);
}
private boolean matchesRoaming(NetworkIdentity ident) {
return (mRoaming == ROAMING_ALL)
|| (mRoaming == ROAMING_YES && ident.mRoaming)
|| (mRoaming == ROAMING_NO && !ident.mRoaming);
}
private boolean matchesDefaultNetwork(NetworkIdentity ident) {
return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
|| (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
|| (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
}
private boolean matchesOemNetwork(NetworkIdentity ident) {
return (mOemManaged == OEM_MANAGED_ALL)
|| (mOemManaged == OEM_MANAGED_YES
&& ident.mOemManaged != OEM_NONE)
|| (mOemManaged == ident.mOemManaged);
}
private boolean matchesCollapsedRatType(NetworkIdentity ident) {
return mSubType == NETWORK_TYPE_ALL
|| getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType);
}
/**
* Check if this template matches {@code subscriberId}. Returns true if this
* template was created with {@code SUBSCRIBER_ID_MATCH_RULE_ALL}, or with a
* {@code mMatchSubscriberIds} array that contains {@code subscriberId}.
*/
public boolean matchesSubscriberId(@Nullable String subscriberId) {
return mSubscriberIdMatchRule == SUBSCRIBER_ID_MATCH_RULE_ALL
|| ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
}
/**
* Check if network with matching SSID. Returns true when the SSID matches, or when
* {@code mNetworkId} is {@code WIFI_NETWORKID_ALL}.
*/
private boolean matchesWifiNetworkId(@Nullable String networkId) {
return Objects.equals(mNetworkId, WIFI_NETWORKID_ALL)
|| Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
}
/**
* Check if mobile network with matching IMSI.
*/
private boolean matchesMobile(NetworkIdentity ident) {
if (ident.mType == TYPE_WIMAX) {
// TODO: consider matching against WiMAX subscriber identity
return true;
} else {
return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
&& ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
&& matchesCollapsedRatType(ident);
}
}
/**
* Get a Radio Access Technology(RAT) type that is representative of a group of RAT types.
* The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}.
*
* @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}.
*/
// TODO: 1. Consider move this to TelephonyManager if used by other modules.
// 2. Consider make this configurable.
// 3. Use TelephonyManager APIs when available.
public static int getCollapsedRatType(int ratType) {
switch (ratType) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_GSM:
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_IDEN:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
return TelephonyManager.NETWORK_TYPE_GSM;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSPAP:
case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
return TelephonyManager.NETWORK_TYPE_UMTS;
case TelephonyManager.NETWORK_TYPE_LTE:
case TelephonyManager.NETWORK_TYPE_IWLAN:
return TelephonyManager.NETWORK_TYPE_LTE;
case TelephonyManager.NETWORK_TYPE_NR:
return TelephonyManager.NETWORK_TYPE_NR;
// Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
case NetworkTemplate.NETWORK_TYPE_5G_NSA:
return NetworkTemplate.NETWORK_TYPE_5G_NSA;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
}
/**
* Return all supported collapsed RAT types that could be returned by
* {@link #getCollapsedRatType(int)}.
*/
@NonNull
public static final int[] getAllCollapsedRatTypes() {
final int[] ratTypes = TelephonyManager.getAllNetworkTypes();
final HashSet<Integer> collapsedRatTypes = new HashSet<>();
for (final int ratType : ratTypes) {
collapsedRatTypes.add(NetworkTemplate.getCollapsedRatType(ratType));
}
// Add NETWORK_TYPE_5G_NSA to the returned list since 5G NSA is a virtual RAT type and
// it is not in TelephonyManager#NETWORK_TYPE_* constants.
// See {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
collapsedRatTypes.add(NetworkTemplate.getCollapsedRatType(NETWORK_TYPE_5G_NSA));
// Ensure that unknown type is returned.
collapsedRatTypes.add(TelephonyManager.NETWORK_TYPE_UNKNOWN);
return toIntArray(collapsedRatTypes);
}
@NonNull
private static int[] toIntArray(@NonNull Collection<Integer> list) {
final int[] array = new int[list.size()];
int i = 0;
for (final Integer item : list) {
array[i++] = item;
}
return array;
}
/**
* Check if matches Wi-Fi network template.
*/
private boolean matchesWifi(NetworkIdentity ident) {
switch (ident.mType) {
case TYPE_WIFI:
return matchesSubscriberId(ident.mSubscriberId)
&& matchesWifiNetworkId(ident.mNetworkId);
default:
return false;
}
}
/**
* Check if matches Ethernet network template.
*/
private boolean matchesEthernet(NetworkIdentity ident) {
if (ident.mType == TYPE_ETHERNET) {
return true;
}
return false;
}
/**
* Check if matches carrier network. The carrier networks means it includes the subscriberId.
*/
private boolean matchesCarrier(NetworkIdentity ident) {
return ident.mSubscriberId != null
&& !ArrayUtils.isEmpty(mMatchSubscriberIds)
&& ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
}
private boolean matchesMobileWildcard(NetworkIdentity ident) {
if (ident.mType == TYPE_WIMAX) {
return true;
} else {
return ident.mType == TYPE_MOBILE && matchesCollapsedRatType(ident);
}
}
private boolean matchesWifiWildcard(NetworkIdentity ident) {
switch (ident.mType) {
case TYPE_WIFI:
case TYPE_WIFI_P2P:
return true;
default:
return false;
}
}
/**
* Check if matches Bluetooth network template.
*/
private boolean matchesBluetooth(NetworkIdentity ident) {
if (ident.mType == TYPE_BLUETOOTH) {
return true;
}
return false;
}
/**
* Check if matches Proxy network template.
*/
private boolean matchesProxy(NetworkIdentity ident) {
return ident.mType == TYPE_PROXY;
}
private static String getMatchRuleName(int matchRule) {
switch (matchRule) {
case MATCH_MOBILE:
return "MOBILE";
case MATCH_WIFI:
return "WIFI";
case MATCH_ETHERNET:
return "ETHERNET";
case MATCH_MOBILE_WILDCARD:
return "MOBILE_WILDCARD";
case MATCH_WIFI_WILDCARD:
return "WIFI_WILDCARD";
case MATCH_BLUETOOTH:
return "BLUETOOTH";
case MATCH_PROXY:
return "PROXY";
case MATCH_CARRIER:
return "CARRIER";
default:
return "UNKNOWN(" + matchRule + ")";
}
}
private static String getOemManagedNames(int oemManaged) {
switch (oemManaged) {
case OEM_MANAGED_ALL:
return "OEM_MANAGED_ALL";
case OEM_MANAGED_NO:
return "OEM_MANAGED_NO";
case OEM_MANAGED_YES:
return "OEM_MANAGED_YES";
default:
return NetworkIdentity.getOemManagedNames(oemManaged);
}
}
/**
* Examine the given template and normalize it.
* We pick the "lowest" merged subscriber as the primary
* for key purposes, and expand the template to match all other merged
* subscribers.
* <p>
* For example, given an incoming template matching B, and the currently
* active merge set [A,B], we'd return a new template that primarily matches
* A, but also matches B.
* TODO: remove and use {@link #normalize(NetworkTemplate, List)}.
*/
@UnsupportedAppUsage
public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
return normalize(template, Arrays.<String[]>asList(merged));
}
/**
* Examine the given template and normalize it.
* We pick the "lowest" merged subscriber as the primary
* for key purposes, and expand the template to match all other merged
* subscribers.
*
* There can be multiple merged subscriberIds for multi-SIM devices.
*
* <p>
* For example, given an incoming template matching B, and the currently
* active merge set [A,B], we'd return a new template that primarily matches
* A, but also matches B.
*/
public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) {
// Now there are several types of network which uses SubscriberId to store network
// information. For instances:
// The TYPE_WIFI with subscriberId means that it is a merged carrier wifi network.
// The TYPE_CARRIER means that the network associate to specific carrier network.
if (template.mSubscriberId == null) return template;
for (String[] merged : mergedList) {
if (ArrayUtils.contains(merged, template.mSubscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
return new NetworkTemplate(template.mMatchRule, merged[0], merged,
template.mNetworkId);
}
}
return template;
}
@UnsupportedAppUsage
public static final @android.annotation.NonNull Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
@Override
public NetworkTemplate createFromParcel(Parcel in) {
return new NetworkTemplate(in);
}
@Override
public NetworkTemplate[] newArray(int size) {
return new NetworkTemplate[size];
}
};
public byte[] getBytesForBackup() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
if (!isPersistable()) {
Log.wtf(TAG, "Trying to backup non-persistable template: " + this);
}
out.writeInt(BACKUP_VERSION);
out.writeInt(mMatchRule);
BackupUtils.writeString(out, mSubscriberId);
BackupUtils.writeString(out, mNetworkId);
out.writeInt(mMetered);
out.writeInt(mSubscriberIdMatchRule);
return baos.toByteArray();
}
public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in)
throws IOException, BackupUtils.BadVersionException {
int version = in.readInt();
if (version < BACKUP_VERSION_1_INIT || version > BACKUP_VERSION) {
throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
}
int matchRule = in.readInt();
String subscriberId = BackupUtils.readString(in);
String networkId = BackupUtils.readString(in);
final int metered;
final int subscriberIdMatchRule;
if (version >= BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) {
metered = in.readInt();
subscriberIdMatchRule = in.readInt();
} else {
// For backward compatibility, fill the missing filters from match rules.
metered = (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD
|| matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL;
subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT;
}
try {
return new NetworkTemplate(matchRule,
subscriberId, new String[] { subscriberId },
networkId, metered, NetworkStats.ROAMING_ALL,
NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL,
NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule);
} catch (IllegalArgumentException e) {
throw new BackupUtils.BadVersionException(
"Restored network template contains unknown match rule " + matchRule, e);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
parcelable UnderlyingNetworkInfo;

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A lightweight container used to carry information on the networks that underly a given
* virtual network.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class UnderlyingNetworkInfo implements Parcelable {
/** The owner of this network. */
private final int mOwnerUid;
/** The interface name of this network. */
@NonNull
private final String mIface;
/** The names of the interfaces underlying this network. */
@NonNull
private final List<String> mUnderlyingIfaces;
public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface,
@NonNull List<String> underlyingIfaces) {
Objects.requireNonNull(iface);
Objects.requireNonNull(underlyingIfaces);
mOwnerUid = ownerUid;
mIface = iface;
mUnderlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
}
private UnderlyingNetworkInfo(@NonNull Parcel in) {
mOwnerUid = in.readInt();
mIface = in.readString();
List<String> underlyingIfaces = new ArrayList<>();
in.readList(underlyingIfaces, null /*classLoader*/);
mUnderlyingIfaces = Collections.unmodifiableList(underlyingIfaces);
}
/** Get the owner of this network. */
public int getOwnerUid() {
return mOwnerUid;
}
/** Get the interface name of this network. */
@NonNull
public String getInterface() {
return mIface;
}
/** Get the names of the interfaces underlying this network. */
@NonNull
public List<String> getUnderlyingInterfaces() {
return mUnderlyingIfaces;
}
@Override
public String toString() {
return "UnderlyingNetworkInfo{"
+ "ownerUid=" + mOwnerUid
+ ", iface='" + mIface + '\''
+ ", underlyingIfaces='" + mUnderlyingIfaces.toString() + '\''
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mOwnerUid);
dest.writeString(mIface);
dest.writeList(mUnderlyingIfaces);
}
@NonNull
public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR =
new Parcelable.Creator<UnderlyingNetworkInfo>() {
@NonNull
@Override
public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) {
return new UnderlyingNetworkInfo(in);
}
@NonNull
@Override
public UnderlyingNetworkInfo[] newArray(int size) {
return new UnderlyingNetworkInfo[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UnderlyingNetworkInfo)) return false;
final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
return mOwnerUid == that.getOwnerUid()
&& Objects.equals(mIface, that.getInterface())
&& Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
}
@Override
public int hashCode() {
return Objects.hash(mOwnerUid, mIface, mUnderlyingIfaces);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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 android.net.netstats.provider;
/**
* Interface for NetworkStatsService to query network statistics and set data limits.
*
* @hide
*/
oneway interface INetworkStatsProvider {
void onRequestStatsUpdate(int token);
void onSetAlert(long quotaBytes);
void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes);
}

View File

@@ -0,0 +1,31 @@
/*
* 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 android.net.netstats.provider;
import android.net.NetworkStats;
/**
* Interface for implementor of {@link INetworkStatsProviderCallback} to push events
* such as network statistics update or notify limit reached.
* @hide
*/
oneway interface INetworkStatsProviderCallback {
void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
void notifyAlertReached();
void notifyWarningOrLimitReached();
void unregister();
}

View File

@@ -0,0 +1,232 @@
/*
* 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 android.net.netstats.provider;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.NetworkStats;
import android.os.RemoteException;
/**
* A base class that allows external modules to implement a custom network statistics provider.
* @hide
*/
@SystemApi
public abstract class NetworkStatsProvider {
/**
* A value used by {@link #onSetLimit}, {@link #onSetAlert} and {@link #onSetWarningAndLimit}
* indicates there is no limit.
*/
public static final int QUOTA_UNLIMITED = -1;
@NonNull private final INetworkStatsProvider mProviderBinder =
new INetworkStatsProvider.Stub() {
@Override
public void onRequestStatsUpdate(int token) {
NetworkStatsProvider.this.onRequestStatsUpdate(token);
}
@Override
public void onSetAlert(long quotaBytes) {
NetworkStatsProvider.this.onSetAlert(quotaBytes);
}
@Override
public void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes) {
NetworkStatsProvider.this.onSetWarningAndLimit(iface, warningBytes, limitBytes);
}
};
// The binder given by the service when successfully registering. Only null before registering,
// never null once non-null.
@Nullable
private INetworkStatsProviderCallback mProviderCbBinder;
/**
* Return the binder invoked by the service and redirect function calls to the overridden
* methods.
* @hide
*/
@NonNull
public INetworkStatsProvider getProviderBinder() {
return mProviderBinder;
}
/**
* Store the binder that was returned by the service when successfully registering. Note that
* the provider cannot be re-registered. Hence this method can only be called once per provider.
*
* @hide
*/
public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
if (mProviderCbBinder != null) {
throw new IllegalArgumentException("provider is already registered");
}
mProviderCbBinder = binder;
}
/**
* Get the binder that was returned by the service when successfully registering. Or null if the
* provider was never registered.
*
* @hide
*/
@Nullable
public INetworkStatsProviderCallback getProviderCallbackBinder() {
return mProviderCbBinder;
}
/**
* Get the binder that was returned by the service when successfully registering. Throw an
* {@link IllegalStateException} if the provider is not registered.
*
* @hide
*/
@NonNull
public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
if (mProviderCbBinder == null) {
throw new IllegalStateException("the provider is not registered");
}
return mProviderCbBinder;
}
/**
* Notify the system of new network statistics.
*
* Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
* be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
* being called. Responding later increases the probability stats will be dropped. The
* provider can also call this whenever it wants to reports new stats for any reason.
* Note that the system will not necessarily immediately propagate the statistics to
* reflect the update.
*
* @param token the token under which these stats were gathered. Providers can call this method
* with the current token as often as they want, until the token changes.
* {@see NetworkStatsProvider#onRequestStatsUpdate()}
* @param ifaceStats the {@link NetworkStats} per interface to be reported.
* The provider should not include any traffic that is already counted by
* kernel interface counters.
* @param uidStats the same stats as above, but counts {@link NetworkStats}
* per uid.
*/
public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
@NonNull NetworkStats uidStats) {
try {
getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the quota set by {@code onSetAlert} has been reached.
*/
public void notifyAlertReached() {
try {
getProviderCallbackBinderOrThrow().notifyAlertReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the warning set by {@link #onSetWarningAndLimit} has been reached.
*/
public void notifyWarningReached() {
try {
// Reuse the code path to notify warning reached with limit reached
// since framework handles them in the same way.
getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the quota set by {@link #onSetLimit} or limit set by
* {@link #onSetWarningAndLimit} has been reached.
*/
public void notifyLimitReached() {
try {
getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Called by {@code NetworkStatsService} when it requires to know updated stats.
* The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
* Responding later increases the probability stats will be dropped. Memory allowing, the
* system will try to take stats into account up to one minute after calling
* {@link #onRequestStatsUpdate}.
*
* @param token a positive number identifying the new state of the system under which
* {@link NetworkStats} have to be gathered from now on. When this is called,
* custom implementations of providers MUST tally and report the latest stats with
* the previous token, under which stats were being gathered so far.
*/
public abstract void onRequestStatsUpdate(int token);
/**
* Called by {@code NetworkStatsService} when setting the interface quota for the specified
* upstream interface. When this is called, the custom implementation should block all egress
* packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
* been reached, and MUST respond to it by calling
* {@link NetworkStatsProvider#notifyLimitReached()}.
*
* @param iface the interface requiring the operation.
* @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
*/
public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
/**
* Called by {@code NetworkStatsService} when setting the interface quotas for the specified
* upstream interface. If a provider implements {@link #onSetWarningAndLimit}, the system
* will not call {@link #onSetLimit}. When this method is called, the implementation
* should behave as follows:
* 1. If {@code warningBytes} is reached on {@code iface}, block all further traffic on
* {@code iface} and call {@link NetworkStatsProvider@notifyWarningReached()}.
* 2. If {@code limitBytes} is reached on {@code iface}, block all further traffic on
* {@code iface} and call {@link NetworkStatsProvider#notifyLimitReached()}.
*
* @param iface the interface requiring the operation.
* @param warningBytes the warning defined as the number of bytes, starting from zero and
* counting from now. A value of {@link #QUOTA_UNLIMITED} indicates
* there is no warning.
* @param limitBytes the limit defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
*/
public void onSetWarningAndLimit(@NonNull String iface, long warningBytes, long limitBytes) {
// Backward compatibility for those who didn't override this function.
onSetLimit(iface, limitBytes);
}
/**
* Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
* MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
* have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
* not block all egress packets.
*
* @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
*/
public abstract void onSetAlert(long quotaBytes);
}