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