/*
 * Copyright (C) 2007 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.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.DownloadManager;
import android.app.backup.BackupManager;
import android.app.usage.NetworkStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.StrictMode;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;

/**
 * Class that provides network traffic statistics. These statistics include
 * bytes transmitted and received and network packets transmitted and received,
 * over all interfaces, over the mobile interface, and on a per-UID basis.
 * <p>
 * These statistics may not be available on all platforms. If the statistics are
 * not supported by this device, {@link #UNSUPPORTED} will be returned.
 * <p>
 * Note that the statistics returned by this class reset and start from zero
 * after every reboot. To access more robust historical network statistics data,
 * use {@link NetworkStatsManager} instead.
 */
public class TrafficStats {
    static {
        System.loadLibrary("framework-connectivity-tiramisu-jni");
    }

    private static final String TAG = TrafficStats.class.getSimpleName();
    /**
     * The return value to indicate that the device does not support the statistic.
     */
    public final static int UNSUPPORTED = -1;

    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long KB_IN_BYTES = 1024;
    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
    @Deprecated
    public static final long PB_IN_BYTES = TB_IN_BYTES * 1024;

    /**
     * Special UID value used when collecting {@link NetworkStatsHistory} for
     * removed applications.
     *
     * @hide
     */
    public static final int UID_REMOVED = -4;

    /**
     * Special UID value used when collecting {@link NetworkStatsHistory} for
     * tethering traffic.
     *
     * @hide
     */
    public static final int UID_TETHERING = NetworkStats.UID_TETHERING;

    /**
     * Tag values in this range are reserved for the network stack. The network stack is
     * running as UID {@link android.os.Process.NETWORK_STACK_UID} when in the mainline
     * module separate process, and as the system UID otherwise.
     */
    /** @hide */
    @SystemApi
    public static final int TAG_NETWORK_STACK_RANGE_START = 0xFFFFFD00;
    /** @hide */
    @SystemApi
    public static final int TAG_NETWORK_STACK_RANGE_END = 0xFFFFFEFF;

    /**
     * Tags between 0xFFFFFF00 and 0xFFFFFFFF are reserved and used internally by system services
     * like DownloadManager when performing traffic on behalf of an application.
     */
    // Please note there is no enforcement of these constants, so do not rely on them to
    // determine that the caller is a system caller.
    /** @hide */
    @SystemApi
    public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = 0xFFFFFF00;
    /** @hide */
    @SystemApi
    public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = 0xFFFFFF0F;

    /**
     * Tag values between these ranges are reserved for the network stack to do traffic
     * on behalf of applications. It is a subrange of the range above.
     */
    /** @hide */
    @SystemApi
    public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = 0xFFFFFF80;
    /** @hide */
    @SystemApi
    public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = 0xFFFFFF8F;

    /**
     * Default tag value for {@link DownloadManager} traffic.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFFFF01;

    /**
     * Default tag value for {@link MediaPlayer} traffic.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_MEDIA = 0xFFFFFF02;

    /**
     * Default tag value for {@link BackupManager} backup traffic; that is,
     * traffic from the device to the storage backend.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_BACKUP = 0xFFFFFF03;

    /**
     * Default tag value for {@link BackupManager} restore traffic; that is,
     * app data retrieved from the storage backend at install time.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04;

    /**
     * Default tag value for code (typically APKs) downloaded by an app store on
     * behalf of the app, such as updates.
     *
     * @hide
     */
    public static final int TAG_SYSTEM_APP = 0xFFFFFF05;

    // TODO : remove this constant when Wifi code is updated
    /** @hide */
    public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42;

    private static INetworkStatsService sStatsService;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
    private synchronized static INetworkStatsService getStatsService() {
        if (sStatsService == null) {
            throw new IllegalStateException("TrafficStats not initialized, uid="
                    + Binder.getCallingUid());
        }
        return sStatsService;
    }

    /**
     * Snapshot of {@link NetworkStats} when the currently active profiling
     * session started, or {@code null} if no session active.
     *
     * @see #startDataProfiling(Context)
     * @see #stopDataProfiling(Context)
     */
    private static NetworkStats sActiveProfilingStart;

    private static Object sProfilingLock = new Object();

    private static final String LOOPBACK_IFACE = "lo";

    /**
     * Initialization {@link TrafficStats} with the context, to
     * allow {@link TrafficStats} to fetch the needed binder.
     *
     * @param context a long-lived context, such as the application context or system
     *                server context.
     * @hide
     */
    @SystemApi(client = MODULE_LIBRARIES)
    @SuppressLint("VisiblySynchronized")
    public static synchronized void init(@NonNull final Context context) {
        if (sStatsService != null) {
            throw new IllegalStateException("TrafficStats is already initialized, uid="
                    + Binder.getCallingUid());
        }
        final NetworkStatsManager statsManager =
                context.getSystemService(NetworkStatsManager.class);
        if (statsManager == null) {
            // TODO: Currently Process.isSupplemental is not working yet, because it depends on
            //  process to run in a certain UID range, which is not true for now. Change this
            //  to Log.wtf once Process.isSupplemental is ready.
            Log.e(TAG, "TrafficStats not initialized, uid=" + Binder.getCallingUid());
            return;
        }
        sStatsService = statsManager.getBinder();
    }

    /**
     * Attach the socket tagger implementation to the current process, to
     * get notified when a socket's {@link FileDescriptor} is assigned to
     * a thread. See {@link SocketTagger#set(SocketTagger)}.
     *
     * @hide
     */
    @SystemApi(client = MODULE_LIBRARIES)
    public static void attachSocketTagger() {
        dalvik.system.SocketTagger.set(new SocketTagger());
    }

    private static class SocketTagger extends dalvik.system.SocketTagger {

        // TODO: set to false
        private static final boolean LOGD = true;

        SocketTagger() {
        }

        @Override
        public void tag(FileDescriptor fd) throws SocketException {
            final UidTag tagInfo = sThreadUidTag.get();
            if (LOGD) {
                Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
                        + Integer.toHexString(tagInfo.tag) + ", statsUid=" + tagInfo.uid);
            }
            if (tagInfo.tag == -1) {
                StrictMode.noteUntaggedSocket();
            }

            if (tagInfo.tag == -1 && tagInfo.uid == -1) return;
            final int errno = native_tagSocketFd(fd, tagInfo.tag, tagInfo.uid);
            if (errno < 0) {
                Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
                        + tagInfo.tag + ", "
                        + tagInfo.uid + ") failed with errno" + errno);
            }
        }

        @Override
        public void untag(FileDescriptor fd) throws SocketException {
            if (LOGD) {
                Log.i(TAG, "untagSocket(" + fd.getInt$() + ")");
            }

            final UidTag tagInfo = sThreadUidTag.get();
            if (tagInfo.tag == -1 && tagInfo.uid == -1) return;

            final int errno = native_untagSocketFd(fd);
            if (errno < 0) {
                Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
            }
        }
    }

    private static native int native_tagSocketFd(FileDescriptor fd, int tag, int uid);
    private static native int native_untagSocketFd(FileDescriptor fd);

    private static class UidTag {
        public int tag = -1;
        public int uid = -1;
    }

    private static ThreadLocal<UidTag> sThreadUidTag = new ThreadLocal<UidTag>() {
        @Override
        protected UidTag initialValue() {
            return new UidTag();
        }
    };

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
     * <p>
     * Changes only take effect during subsequent calls to
     * {@link #tagSocket(Socket)}.
     * <p>
     * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
     * used internally by system services like {@link DownloadManager} when
     * performing traffic on behalf of an application.
     *
     * @see #clearThreadStatsTag()
     */
    public static void setThreadStatsTag(int tag) {
        getAndSetThreadStatsTag(tag);
    }

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
     * <p>
     * Changes only take effect during subsequent calls to
     * {@link #tagSocket(Socket)}.
     * <p>
     * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
     * used internally by system services like {@link DownloadManager} when
     * performing traffic on behalf of an application.
     *
     * @return the current tag for the calling thread, which can be used to
     *         restore any existing values after a nested operation is finished
     */
    public static int getAndSetThreadStatsTag(int tag) {
        final int old = sThreadUidTag.get().tag;
        sThreadUidTag.get().tag = tag;
        return old;
    }

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. The tag used internally is well-defined to
     * distinguish all backup-related traffic.
     *
     * @hide
     */
    @SystemApi
    public static void setThreadStatsTagBackup() {
        setThreadStatsTag(TAG_SYSTEM_BACKUP);
    }

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. The tag used internally is well-defined to
     * distinguish all restore-related traffic.
     *
     * @hide
     */
    @SystemApi
    public static void setThreadStatsTagRestore() {
        setThreadStatsTag(TAG_SYSTEM_RESTORE);
    }

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. The tag used internally is well-defined to
     * distinguish all code (typically APKs) downloaded by an app store on
     * behalf of the app, such as updates.
     *
     * @hide
     */
    @SystemApi
    public static void setThreadStatsTagApp() {
        setThreadStatsTag(TAG_SYSTEM_APP);
    }

    /**
     * Set active tag to use when accounting {@link Socket} traffic originating
     * from the current thread. The tag used internally is well-defined to
     * distinguish all download provider traffic.
     *
     * @hide
     */
    @SystemApi(client = MODULE_LIBRARIES)
    public static void setThreadStatsTagDownload() {
        setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
    }

    /**
     * Get the active tag used when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
     * {@link #tagSocket(Socket)}.
     *
     * @see #setThreadStatsTag(int)
     */
    public static int getThreadStatsTag() {
        return sThreadUidTag.get().tag;
    }

    /**
     * Clear any active tag set to account {@link Socket} traffic originating
     * from the current thread.
     *
     * @see #setThreadStatsTag(int)
     */
    public static void clearThreadStatsTag() {
        sThreadUidTag.get().tag = -1;
    }

    /**
     * Set specific UID to use when accounting {@link Socket} traffic
     * originating from the current thread. Designed for use when performing an
     * operation on behalf of another application, or when another application
     * is performing operations on your behalf.
     * <p>
     * Any app can <em>accept</em> blame for traffic performed on a socket
     * originally created by another app by calling this method with the
     * {@link android.system.Os#getuid()} value. However, only apps holding the
     * {@code android.Manifest.permission#UPDATE_DEVICE_STATS} permission may
     * <em>assign</em> blame to another UIDs.
     * <p>
     * Changes only take effect during subsequent calls to
     * {@link #tagSocket(Socket)}.
     */
    @SuppressLint("RequiresPermission")
    public static void setThreadStatsUid(int uid) {
        sThreadUidTag.get().uid = uid;
    }

    /**
     * Get the active UID used when accounting {@link Socket} traffic originating
     * from the current thread. Only one active tag per thread is supported.
     * {@link #tagSocket(Socket)}.
     *
     * @see #setThreadStatsUid(int)
     */
    public static int getThreadStatsUid() {
        return sThreadUidTag.get().uid;
    }

    /**
     * Set specific UID to use when accounting {@link Socket} traffic
     * originating from the current thread as the calling UID. Designed for use
     * when another application is performing operations on your behalf.
     * <p>
     * Changes only take effect during subsequent calls to
     * {@link #tagSocket(Socket)}.
     *
     * @removed
     * @deprecated use {@link #setThreadStatsUid(int)} instead.
     */
    @Deprecated
    public static void setThreadStatsUidSelf() {
        setThreadStatsUid(android.os.Process.myUid());
    }

    /**
     * Clear any active UID set to account {@link Socket} traffic originating
     * from the current thread.
     *
     * @see #setThreadStatsUid(int)
     */
    @SuppressLint("RequiresPermission")
    public static void clearThreadStatsUid() {
        setThreadStatsUid(-1);
    }

    /**
     * Tag the given {@link Socket} with any statistics parameters active for
     * the current thread. Subsequent calls always replace any existing
     * parameters. When finished, call {@link #untagSocket(Socket)} to remove
     * statistics parameters.
     *
     * @see #setThreadStatsTag(int)
     */
    public static void tagSocket(@NonNull Socket socket) throws SocketException {
        SocketTagger.get().tag(socket);
    }

    /**
     * Remove any statistics parameters from the given {@link Socket}.
     * <p>
     * In Android 8.1 (API level 27) and lower, a socket is automatically
     * untagged when it's sent to another process using binder IPC with a
     * {@code ParcelFileDescriptor} container. In Android 9.0 (API level 28)
     * and higher, the socket tag is kept when the socket is sent to another
     * process using binder IPC. You can mimic the previous behavior by
     * calling {@code untagSocket()} before sending the socket to another
     * process.
     */
    public static void untagSocket(@NonNull Socket socket) throws SocketException {
        SocketTagger.get().untag(socket);
    }

    /**
     * Tag the given {@link DatagramSocket} with any statistics parameters
     * active for the current thread. Subsequent calls always replace any
     * existing parameters. When finished, call
     * {@link #untagDatagramSocket(DatagramSocket)} to remove statistics
     * parameters.
     *
     * @see #setThreadStatsTag(int)
     */
    public static void tagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
        SocketTagger.get().tag(socket);
    }

    /**
     * Remove any statistics parameters from the given {@link DatagramSocket}.
     */
    public static void untagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
        SocketTagger.get().untag(socket);
    }

    /**
     * Tag the given {@link FileDescriptor} socket with any statistics
     * parameters active for the current thread. Subsequent calls always replace
     * any existing parameters. When finished, call
     * {@link #untagFileDescriptor(FileDescriptor)} to remove statistics
     * parameters.
     *
     * @see #setThreadStatsTag(int)
     */
    public static void tagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
        SocketTagger.get().tag(fd);
    }

    /**
     * Remove any statistics parameters from the given {@link FileDescriptor}
     * socket.
     */
    public static void untagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
        SocketTagger.get().untag(fd);
    }

    /**
     * Start profiling data usage for current UID. Only one profiling session
     * can be active at a time.
     *
     * @hide
     */
    public static void startDataProfiling(Context context) {
        synchronized (sProfilingLock) {
            if (sActiveProfilingStart != null) {
                throw new IllegalStateException("already profiling data");
            }

            // take snapshot in time; we calculate delta later
            sActiveProfilingStart = getDataLayerSnapshotForUid(context);
        }
    }

    /**
     * Stop profiling data usage for current UID.
     *
     * @return Detailed {@link NetworkStats} of data that occurred since last
     *         {@link #startDataProfiling(Context)} call.
     * @hide
     */
    public static NetworkStats stopDataProfiling(Context context) {
        synchronized (sProfilingLock) {
            if (sActiveProfilingStart == null) {
                throw new IllegalStateException("not profiling data");
            }

            // subtract starting values and return delta
            final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
            final NetworkStats profilingDelta = NetworkStats.subtract(
                    profilingStop, sActiveProfilingStart, null, null);
            sActiveProfilingStart = null;
            return profilingDelta;
        }
    }

    /**
     * Increment count of network operations performed under the accounting tag
     * currently active on the calling thread. This can be used to derive
     * bytes-per-operation.
     *
     * @param operationCount Number of operations to increment count by.
     */
    public static void incrementOperationCount(int operationCount) {
        final int tag = getThreadStatsTag();
        incrementOperationCount(tag, operationCount);
    }

    /**
     * Increment count of network operations performed under the given
     * accounting tag. This can be used to derive bytes-per-operation.
     *
     * @param tag Accounting tag used in {@link #setThreadStatsTag(int)}.
     * @param operationCount Number of operations to increment count by.
     */
    public static void incrementOperationCount(int tag, int operationCount) {
        final int uid = android.os.Process.myUid();
        try {
            getStatsService().incrementOperationCount(uid, tag, operationCount);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** {@hide} */
    public static void closeQuietly(INetworkStatsSession session) {
        // TODO: move to NetworkStatsService once it exists
        if (session != null) {
            try {
                session.close();
            } catch (RuntimeException rethrown) {
                throw rethrown;
            } catch (Exception ignored) {
            }
        }
    }

    private static long addIfSupported(long stat) {
        return (stat == UNSUPPORTED) ? 0 : stat;
    }

    /**
     * Return number of packets transmitted across mobile networks since device
     * boot. Counts packets across all mobile network interfaces, and always
     * increases monotonically since device boot. Statistics are measured at the
     * network layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getMobileTxPackets() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            total += addIfSupported(getTxPackets(iface));
        }
        return total;
    }

    /**
     * Return number of packets received across mobile networks since device
     * boot. Counts packets across all mobile network interfaces, and always
     * increases monotonically since device boot. Statistics are measured at the
     * network layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getMobileRxPackets() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            total += addIfSupported(getRxPackets(iface));
        }
        return total;
    }

    /**
     * Return number of bytes transmitted across mobile networks since device
     * boot. Counts packets across all mobile network interfaces, and always
     * increases monotonically since device boot. Statistics are measured at the
     * network layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getMobileTxBytes() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            total += addIfSupported(getTxBytes(iface));
        }
        return total;
    }

    /**
     * Return number of bytes received across mobile networks since device boot.
     * Counts packets across all mobile network interfaces, and always increases
     * monotonically since device boot. Statistics are measured at the network
     * layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getMobileRxBytes() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            total += addIfSupported(getRxBytes(iface));
        }
        return total;
    }

    /** {@hide} */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public static long getMobileTcpRxPackets() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            long stat = UNSUPPORTED;
            try {
                stat = getStatsService().getIfaceStats(iface, TYPE_TCP_RX_PACKETS);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            total += addIfSupported(stat);
        }
        return total;
    }

    /** {@hide} */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public static long getMobileTcpTxPackets() {
        long total = 0;
        for (String iface : getMobileIfaces()) {
            long stat = UNSUPPORTED;
            try {
                stat = getStatsService().getIfaceStats(iface, TYPE_TCP_TX_PACKETS);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            total += addIfSupported(stat);
        }
        return total;
    }

    /**
     * Return the number of packets transmitted on the specified interface since the interface
     * was created. Statistics are measured at the network layer, so both TCP and
     * UDP usage are included.
     *
     * Note that the returned values are partial statistics that do not count data from several
     * sources and do not apply several adjustments that are necessary for correctness, such
     * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
     * determine whether traffic is being transferred on the specific interface but are not a
     * substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
     * APIs.
     *
     * @param iface The name of the interface.
     * @return The number of transmitted packets.
     */
    public static long getTxPackets(@NonNull String iface) {
        try {
            return getStatsService().getIfaceStats(iface, TYPE_TX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return the number of packets received on the specified interface since the interface was
     * created. Statistics are measured at the network layer, so both TCP
     * and UDP usage are included.
     *
     * Note that the returned values are partial statistics that do not count data from several
     * sources and do not apply several adjustments that are necessary for correctness, such
     * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
     * determine whether traffic is being transferred on the specific interface but are not a
     * substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
     * APIs.
     *
     * @param iface The name of the interface.
     * @return The number of received packets.
     */
    public static long getRxPackets(@NonNull String iface) {
        try {
            return getStatsService().getIfaceStats(iface, TYPE_RX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return the number of bytes transmitted on the specified interface since the interface
     * was created. Statistics are measured at the network layer, so both TCP and
     * UDP usage are included.
     *
     * Note that the returned values are partial statistics that do not count data from several
     * sources and do not apply several adjustments that are necessary for correctness, such
     * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
     * determine whether traffic is being transferred on the specific interface but are not a
     * substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
     * APIs.
     *
     * @param iface The name of the interface.
     * @return The number of transmitted bytes.
     */
    public static long getTxBytes(@NonNull String iface) {
        try {
            return getStatsService().getIfaceStats(iface, TYPE_TX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return the number of bytes received on the specified interface since the interface
     * was created. Statistics are measured at the network layer, so both TCP
     * and UDP usage are included.
     *
     * Note that the returned values are partial statistics that do not count data from several
     * sources and do not apply several adjustments that are necessary for correctness, such
     * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to
     * determine whether traffic is being transferred on the specific interface but are not a
     * substitute for the more accurate statistics provided by the {@link NetworkStatsManager}
     * APIs.
     *
     * @param iface The name of the interface.
     * @return The number of received bytes.
     */
    public static long getRxBytes(@NonNull String iface) {
        try {
            return getStatsService().getIfaceStats(iface, TYPE_RX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackTxPackets() {
        try {
            return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackRxPackets() {
        try {
            return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackTxBytes() {
        try {
            return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** {@hide} */
    @TestApi
    public static long getLoopbackRxBytes() {
        try {
            return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of packets transmitted since device boot. Counts packets
     * across all network interfaces, and always increases monotonically since
     * device boot. Statistics are measured at the network layer, so they
     * include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getTotalTxPackets() {
        try {
            return getStatsService().getTotalStats(TYPE_TX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of packets received since device boot. Counts packets
     * across all network interfaces, and always increases monotonically since
     * device boot. Statistics are measured at the network layer, so they
     * include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getTotalRxPackets() {
        try {
            return getStatsService().getTotalStats(TYPE_RX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of bytes transmitted since device boot. Counts packets
     * across all network interfaces, and always increases monotonically since
     * device boot. Statistics are measured at the network layer, so they
     * include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getTotalTxBytes() {
        try {
            return getStatsService().getTotalStats(TYPE_TX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of bytes received since device boot. Counts packets across
     * all network interfaces, and always increases monotonically since device
     * boot. Statistics are measured at the network layer, so they include both
     * TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     */
    public static long getTotalRxBytes() {
        try {
            return getStatsService().getTotalStats(TYPE_RX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of bytes transmitted by the given UID since device boot.
     * Counts packets across all network interfaces, and always increases
     * monotonically since device boot. Statistics are measured at the network
     * layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
     * <p>
     * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
     * report traffic statistics for the calling UID. It will return
     * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
     * historical network statistics belonging to other UIDs, use
     * {@link NetworkStatsManager}.
     *
     * @see android.os.Process#myUid()
     * @see android.content.pm.ApplicationInfo#uid
     */
    public static long getUidTxBytes(int uid) {
        try {
            return getStatsService().getUidStats(uid, TYPE_TX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of bytes received by the given UID since device boot.
     * Counts packets across all network interfaces, and always increases
     * monotonically since device boot. Statistics are measured at the network
     * layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
     * {@link #UNSUPPORTED} on devices where statistics aren't available.
     * <p>
     * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
     * report traffic statistics for the calling UID. It will return
     * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
     * historical network statistics belonging to other UIDs, use
     * {@link NetworkStatsManager}.
     *
     * @see android.os.Process#myUid()
     * @see android.content.pm.ApplicationInfo#uid
     */
    public static long getUidRxBytes(int uid) {
        try {
            return getStatsService().getUidStats(uid, TYPE_RX_BYTES);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of packets transmitted by the given UID since device boot.
     * Counts packets across all network interfaces, and always increases
     * monotonically since device boot. Statistics are measured at the network
     * layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
     * {@link #UNSUPPORTED} on devices where statistics aren't available.
     * <p>
     * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
     * report traffic statistics for the calling UID. It will return
     * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
     * historical network statistics belonging to other UIDs, use
     * {@link NetworkStatsManager}.
     *
     * @see android.os.Process#myUid()
     * @see android.content.pm.ApplicationInfo#uid
     */
    public static long getUidTxPackets(int uid) {
        try {
            return getStatsService().getUidStats(uid, TYPE_TX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return number of packets received by the given UID since device boot.
     * Counts packets across all network interfaces, and always increases
     * monotonically since device boot. Statistics are measured at the network
     * layer, so they include both TCP and UDP usage.
     * <p>
     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
     * {@link #UNSUPPORTED} on devices where statistics aren't available.
     * <p>
     * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
     * report traffic statistics for the calling UID. It will return
     * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
     * historical network statistics belonging to other UIDs, use
     * {@link NetworkStatsManager}.
     *
     * @see android.os.Process#myUid()
     * @see android.content.pm.ApplicationInfo#uid
     */
    public static long getUidRxPackets(int uid) {
        try {
            return getStatsService().getUidStats(uid, TYPE_RX_PACKETS);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidTxBytes(int)
     */
    @Deprecated
    public static long getUidTcpTxBytes(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidRxBytes(int)
     */
    @Deprecated
    public static long getUidTcpRxBytes(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidTxBytes(int)
     */
    @Deprecated
    public static long getUidUdpTxBytes(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidRxBytes(int)
     */
    @Deprecated
    public static long getUidUdpRxBytes(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidTxPackets(int)
     */
    @Deprecated
    public static long getUidTcpTxSegments(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidRxPackets(int)
     */
    @Deprecated
    public static long getUidTcpRxSegments(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidTxPackets(int)
     */
    @Deprecated
    public static long getUidUdpTxPackets(int uid) {
        return UNSUPPORTED;
    }

    /**
     * @deprecated Starting in {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     *             transport layer statistics are no longer available, and will
     *             always return {@link #UNSUPPORTED}.
     * @see #getUidRxPackets(int)
     */
    @Deprecated
    public static long getUidUdpRxPackets(int uid) {
        return UNSUPPORTED;
    }

    /**
     * Return detailed {@link NetworkStats} for the current UID. Requires no
     * special permission.
     */
    private static NetworkStats getDataLayerSnapshotForUid(Context context) {
        // TODO: take snapshot locally, since proc file is now visible
        final int uid = android.os.Process.myUid();
        try {
            return getStatsService().getDataLayerSnapshotForUid(uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Return set of any ifaces associated with mobile networks since boot.
     * Interfaces are never removed from this list, so counters should always be
     * monotonic.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
    private static String[] getMobileIfaces() {
        try {
            return getStatsService().getMobileIfaces();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    // NOTE: keep these in sync with {@code com_android_server_net_NetworkStatsService.cpp}.
    /** {@hide} */
    public static final int TYPE_RX_BYTES = 0;
    /** {@hide} */
    public static final int TYPE_RX_PACKETS = 1;
    /** {@hide} */
    public static final int TYPE_TX_BYTES = 2;
    /** {@hide} */
    public static final int TYPE_TX_PACKETS = 3;
    /** {@hide} */
    public static final int TYPE_TCP_RX_PACKETS = 4;
    /** {@hide} */
    public static final int TYPE_TCP_TX_PACKETS = 5;
}
