/** * 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.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. *
* 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. *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()}. *
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. *
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()}. *
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. *
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()}. *
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}. *
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. *
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()}. *
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}. *
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. *
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()}. *
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. * *
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. *
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()}. *
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