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