1 /*
2  * Copyright (C) 2015 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.settingslib.net;
18 
19 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
20 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
21 import static android.net.TrafficStats.MB_IN_BYTES;
22 import static android.telephony.TelephonyManager.SIM_STATE_READY;
23 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
24 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
25 
26 import android.app.usage.NetworkStats.Bucket;
27 import android.app.usage.NetworkStatsManager;
28 import android.content.Context;
29 import android.net.ConnectivityManager;
30 import android.net.INetworkStatsService;
31 import android.net.INetworkStatsSession;
32 import android.net.NetworkPolicy;
33 import android.net.NetworkPolicyManager;
34 import android.net.NetworkTemplate;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.telephony.SubscriptionManager;
38 import android.telephony.TelephonyManager;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.util.Range;
42 
43 import com.android.internal.R;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.util.ArrayUtils;
46 
47 import java.time.ZonedDateTime;
48 import java.util.Iterator;
49 import java.util.Locale;
50 
51 public class DataUsageController {
52 
53     private static final String TAG = "DataUsageController";
54     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
55     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
56     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
57     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
58             PERIOD_BUILDER, Locale.getDefault());
59 
60     private final Context mContext;
61     private final INetworkStatsService mStatsService;
62     private final NetworkPolicyManager mPolicyManager;
63     private final NetworkStatsManager mNetworkStatsManager;
64 
65     private INetworkStatsSession mSession;
66     private Callback mCallback;
67     private NetworkNameProvider mNetworkController;
68     private int mSubscriptionId;
69 
DataUsageController(Context context)70     public DataUsageController(Context context) {
71         mContext = context;
72         mStatsService = INetworkStatsService.Stub.asInterface(
73                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
74         mPolicyManager = NetworkPolicyManager.from(mContext);
75         mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
76         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
77     }
78 
setNetworkController(NetworkNameProvider networkController)79     public void setNetworkController(NetworkNameProvider networkController) {
80         mNetworkController = networkController;
81     }
82 
83     /**
84      * By default this class will just get data usage information for the default data subscription,
85      * but this method can be called to require it to use an explicit subscription id which may be
86      * different from the default one (this is useful for the case of multi-SIM devices).
87      */
setSubscriptionId(int subscriptionId)88     public void setSubscriptionId(int subscriptionId) {
89         mSubscriptionId = subscriptionId;
90     }
91 
92     /**
93      * Returns the default warning level in bytes.
94      */
getDefaultWarningLevel()95     public long getDefaultWarningLevel() {
96         return MB_IN_BYTES
97                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
98     }
99 
setCallback(Callback callback)100     public void setCallback(Callback callback) {
101         mCallback = callback;
102     }
103 
warn(String msg)104     private DataUsageInfo warn(String msg) {
105         Log.w(TAG, "Failed to get data usage, " + msg);
106         return null;
107     }
108 
getDataUsageInfo()109     public DataUsageInfo getDataUsageInfo() {
110         NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
111 
112         return getDataUsageInfo(template);
113     }
114 
getWifiDataUsageInfo()115     public DataUsageInfo getWifiDataUsageInfo() {
116         NetworkTemplate template = NetworkTemplate.buildTemplateWifi(
117                 NetworkTemplate.WIFI_NETWORKID_ALL, null);
118         return getDataUsageInfo(template);
119     }
120 
getDataUsageInfo(NetworkTemplate template)121     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
122         final NetworkPolicy policy = findNetworkPolicy(template);
123         final long now = System.currentTimeMillis();
124         final long start, end;
125         final Iterator<Range<ZonedDateTime>> it = (policy != null) ? policy.cycleIterator() : null;
126         if (it != null && it.hasNext()) {
127             final Range<ZonedDateTime> cycle = it.next();
128             start = cycle.getLower().toInstant().toEpochMilli();
129             end = cycle.getUpper().toInstant().toEpochMilli();
130         } else {
131             // period = last 4 wks
132             end = now;
133             start = now - DateUtils.WEEK_IN_MILLIS * 4;
134         }
135         final long totalBytes = getUsageLevel(template, start, end);
136         if (totalBytes < 0L) {
137             return warn("no entry data");
138         }
139         final DataUsageInfo usage = new DataUsageInfo();
140         usage.startDate = start;
141         usage.usageLevel = totalBytes;
142         usage.period = formatDateRange(start, end);
143         usage.cycleStart = start;
144         usage.cycleEnd = end;
145 
146         if (policy != null) {
147             usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
148             usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
149         } else {
150             usage.warningLevel = getDefaultWarningLevel();
151         }
152         if (usage != null && mNetworkController != null) {
153             usage.carrier = mNetworkController.getMobileDataNetworkName();
154         }
155         return usage;
156     }
157 
158     /**
159      * Get the total usage level recorded in the network history
160      * @param template the network template to retrieve the network history
161      * @return the total usage level recorded in the network history or -1L if there is error
162      * retrieving the data.
163      */
getHistoricalUsageLevel(NetworkTemplate template)164     public long getHistoricalUsageLevel(NetworkTemplate template) {
165         return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
166     }
167 
getUsageLevel(NetworkTemplate template, long start, long end)168     private long getUsageLevel(NetworkTemplate template, long start, long end) {
169         try {
170             final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(template, start, end);
171             if (bucket != null) {
172                 return bucket.getRxBytes() + bucket.getTxBytes();
173             }
174             Log.w(TAG, "Failed to get data usage, no entry data");
175         } catch (RemoteException e) {
176             Log.w(TAG, "Failed to get data usage, remote call failed");
177         }
178         return -1L;
179     }
180 
findNetworkPolicy(NetworkTemplate template)181     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
182         if (mPolicyManager == null || template == null) return null;
183         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
184         if (policies == null) return null;
185         final int N = policies.length;
186         for (int i = 0; i < N; i++) {
187             final NetworkPolicy policy = policies[i];
188             if (policy != null && template.equals(policy.template)) {
189                 return policy;
190             }
191         }
192         return null;
193     }
194 
statsBucketToString(Bucket bucket)195     private static String statsBucketToString(Bucket bucket) {
196         return bucket == null ? null : new StringBuilder("Entry[")
197             .append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp())
198             .append(",bucketStart=").append(bucket.getStartTimeStamp())
199             .append(",rxBytes=").append(bucket.getRxBytes())
200             .append(",rxPackets=").append(bucket.getRxPackets())
201             .append(",txBytes=").append(bucket.getTxBytes())
202             .append(",txPackets=").append(bucket.getTxPackets())
203             .append(']').toString();
204     }
205 
206     @VisibleForTesting
getTelephonyManager()207     public TelephonyManager getTelephonyManager() {
208         int subscriptionId = mSubscriptionId;
209 
210         // If mSubscriptionId is invalid, get default data sub.
211         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
212             subscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
213         }
214 
215         // If data sub is also invalid, get any active sub.
216         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
217             int[] activeSubIds = SubscriptionManager.from(mContext).getActiveSubscriptionIdList();
218             if (!ArrayUtils.isEmpty(activeSubIds)) {
219                 subscriptionId = activeSubIds[0];
220             }
221         }
222 
223         return mContext.getSystemService(
224                 TelephonyManager.class).createForSubscriptionId(subscriptionId);
225     }
226 
setMobileDataEnabled(boolean enabled)227     public void setMobileDataEnabled(boolean enabled) {
228         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
229         getTelephonyManager().setDataEnabled(enabled);
230         if (mCallback != null) {
231             mCallback.onMobileDataEnabled(enabled);
232         }
233     }
234 
isMobileDataSupported()235     public boolean isMobileDataSupported() {
236         // require both supported network and ready SIM
237         return getTelephonyManager().isDataCapable()
238                 && getTelephonyManager().getSimState() == SIM_STATE_READY;
239     }
240 
isMobileDataEnabled()241     public boolean isMobileDataEnabled() {
242         return getTelephonyManager().isDataEnabled();
243     }
244 
getNetworkType(NetworkTemplate networkTemplate)245     static int getNetworkType(NetworkTemplate networkTemplate) {
246         if (networkTemplate == null) {
247             return ConnectivityManager.TYPE_NONE;
248         }
249         final int matchRule = networkTemplate.getMatchRule();
250         switch (matchRule) {
251             case NetworkTemplate.MATCH_MOBILE:
252             case NetworkTemplate.MATCH_MOBILE_WILDCARD:
253                 return ConnectivityManager.TYPE_MOBILE;
254             case NetworkTemplate.MATCH_WIFI:
255             case NetworkTemplate.MATCH_WIFI_WILDCARD:
256                 return  ConnectivityManager.TYPE_WIFI;
257             case NetworkTemplate.MATCH_ETHERNET:
258                 return  ConnectivityManager.TYPE_ETHERNET;
259             default:
260                 return ConnectivityManager.TYPE_MOBILE;
261         }
262     }
263 
getActiveSubscriberId()264     private String getActiveSubscriberId() {
265         final String actualSubscriberId = getTelephonyManager().getSubscriberId();
266         return actualSubscriberId;
267     }
268 
formatDateRange(long start, long end)269     private String formatDateRange(long start, long end) {
270         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
271         synchronized (PERIOD_BUILDER) {
272             PERIOD_BUILDER.setLength(0);
273             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
274                     .toString();
275         }
276     }
277 
278     public interface NetworkNameProvider {
getMobileDataNetworkName()279         String getMobileDataNetworkName();
280     }
281 
282     public static class DataUsageInfo {
283         public String carrier;
284         public String period;
285         public long startDate;
286         public long limitLevel;
287         public long warningLevel;
288         public long usageLevel;
289         public long cycleStart;
290         public long cycleEnd;
291     }
292 
293     public interface Callback {
onMobileDataEnabled(boolean enabled)294         void onMobileDataEnabled(boolean enabled);
295     }
296 }
297