1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.net.module.util;
18 
19 import android.app.usage.NetworkStats;
20 
21 import com.android.internal.annotations.VisibleForTesting;
22 
23 /**
24  * Various utilities used for NetworkStats related code.
25  *
26  * @hide
27  */
28 public class NetworkStatsUtils {
29     // These constants must be synced with the definition in android.net.NetworkStats.
30     // TODO: update to formal APIs once all downstreams have these APIs.
31     private static final int SET_ALL = -1;
32     private static final int METERED_ALL = -1;
33     private static final int ROAMING_ALL = -1;
34     private static final int DEFAULT_NETWORK_ALL = -1;
35 
36     /**
37      * Safely multiple a value by a rational.
38      * <p>
39      * Internally it uses integer-based math whenever possible, but switches
40      * over to double-based math if values would overflow.
41      * @hide
42      */
multiplySafeByRational(long value, long num, long den)43     public static long multiplySafeByRational(long value, long num, long den) {
44         if (den == 0) {
45             throw new ArithmeticException("Invalid Denominator");
46         }
47         long x = value;
48         long y = num;
49 
50         // Logic shamelessly borrowed from Math.multiplyExact()
51         long r = x * y;
52         long ax = Math.abs(x);
53         long ay = Math.abs(y);
54         if (((ax | ay) >>> 31 != 0)) {
55             // Some bits greater than 2^31 that might cause overflow
56             // Check the result using the divide operator
57             // and check for the special case of Long.MIN_VALUE * -1
58             if (((y != 0) && (r / y != x))
59                     || (x == Long.MIN_VALUE && y == -1)) {
60                 // Use double math to avoid overflowing
61                 return (long) (((double) num / den) * value);
62             }
63         }
64         return r / den;
65     }
66 
67     /**
68      * Value of the match rule of the subscriberId to match networks with specific subscriberId.
69      *
70      * @hide
71      */
72     public static final int SUBSCRIBER_ID_MATCH_RULE_EXACT = 0;
73     /**
74      * Value of the match rule of the subscriberId to match networks with any subscriberId which
75      * includes null and non-null.
76      *
77      * @hide
78      */
79     public static final int SUBSCRIBER_ID_MATCH_RULE_ALL = 1;
80 
81     /**
82      * Name representing {@link #bandwidthSetGlobalAlert(long)} limit when delivered to
83      * {@link AlertObserver#onQuotaLimitReached(String, String)}.
84      */
85     public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
86 
87     /**
88      * Return the constrained value by given the lower and upper bounds.
89      */
constrain(int amount, int low, int high)90     public static int constrain(int amount, int low, int high) {
91         if (low > high) throw new IllegalArgumentException("low(" + low + ") > high(" + high + ")");
92         return amount < low ? low : (amount > high ? high : amount);
93     }
94 
95     /**
96      * Return the constrained value by given the lower and upper bounds.
97      */
constrain(long amount, long low, long high)98     public static long constrain(long amount, long low, long high) {
99         if (low > high) throw new IllegalArgumentException("low(" + low + ") > high(" + high + ")");
100         return amount < low ? low : (amount > high ? high : amount);
101     }
102 
103     /**
104      * Convert structure from android.app.usage.NetworkStats to android.net.NetworkStats.
105      */
fromPublicNetworkStats( NetworkStats publiceNetworkStats)106     public static android.net.NetworkStats fromPublicNetworkStats(
107             NetworkStats publiceNetworkStats) {
108         android.net.NetworkStats stats = new android.net.NetworkStats(0L, 0);
109         while (publiceNetworkStats.hasNextBucket()) {
110             NetworkStats.Bucket bucket = new NetworkStats.Bucket();
111             publiceNetworkStats.getNextBucket(bucket);
112             final android.net.NetworkStats.Entry entry = fromBucket(bucket);
113             stats = stats.addEntry(entry);
114         }
115         return stats;
116     }
117 
118     @VisibleForTesting
fromBucket(NetworkStats.Bucket bucket)119     public static android.net.NetworkStats.Entry fromBucket(NetworkStats.Bucket bucket) {
120         return new android.net.NetworkStats.Entry(
121                 null /* IFACE_ALL */, bucket.getUid(), convertBucketState(bucket.getState()),
122                 convertBucketTag(bucket.getTag()), convertBucketMetered(bucket.getMetered()),
123                 convertBucketRoaming(bucket.getRoaming()),
124                 convertBucketDefaultNetworkStatus(bucket.getDefaultNetworkStatus()),
125                 bucket.getRxBytes(), bucket.getRxPackets(),
126                 bucket.getTxBytes(), bucket.getTxPackets(), 0 /* operations */);
127     }
128 
convertBucketState(int networkStatsSet)129     private static int convertBucketState(int networkStatsSet) {
130         switch (networkStatsSet) {
131             case NetworkStats.Bucket.STATE_ALL: return SET_ALL;
132             case NetworkStats.Bucket.STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
133             case NetworkStats.Bucket.STATE_FOREGROUND:
134                 return android.net.NetworkStats.SET_FOREGROUND;
135         }
136         return 0;
137     }
138 
convertBucketTag(int tag)139     private static int convertBucketTag(int tag) {
140         switch (tag) {
141             case NetworkStats.Bucket.TAG_NONE: return android.net.NetworkStats.TAG_NONE;
142         }
143         return tag;
144     }
145 
convertBucketMetered(int metered)146     private static int convertBucketMetered(int metered) {
147         switch (metered) {
148             case NetworkStats.Bucket.METERED_ALL: return METERED_ALL;
149             case NetworkStats.Bucket.METERED_NO: return android.net.NetworkStats.METERED_NO;
150             case NetworkStats.Bucket.METERED_YES: return android.net.NetworkStats.METERED_YES;
151         }
152         return 0;
153     }
154 
convertBucketRoaming(int roaming)155     private static int convertBucketRoaming(int roaming) {
156         switch (roaming) {
157             case NetworkStats.Bucket.ROAMING_ALL: return ROAMING_ALL;
158             case NetworkStats.Bucket.ROAMING_NO: return android.net.NetworkStats.ROAMING_NO;
159             case NetworkStats.Bucket.ROAMING_YES: return android.net.NetworkStats.ROAMING_YES;
160         }
161         return 0;
162     }
163 
convertBucketDefaultNetworkStatus(int defaultNetworkStatus)164     private static int convertBucketDefaultNetworkStatus(int defaultNetworkStatus) {
165         switch (defaultNetworkStatus) {
166             case NetworkStats.Bucket.DEFAULT_NETWORK_ALL:
167                 return DEFAULT_NETWORK_ALL;
168             case NetworkStats.Bucket.DEFAULT_NETWORK_NO:
169                 return android.net.NetworkStats.DEFAULT_NETWORK_NO;
170             case NetworkStats.Bucket.DEFAULT_NETWORK_YES:
171                 return android.net.NetworkStats.DEFAULT_NETWORK_YES;
172         }
173         return 0;
174     }
175 }
176