• 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.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.content.Context;
28 import android.net.ConnectivityManager;
29 import android.net.INetworkStatsService;
30 import android.net.INetworkStatsSession;
31 import android.net.NetworkPolicy;
32 import android.net.NetworkPolicyManager;
33 import android.net.NetworkStatsHistory;
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.Pair;
42 
43 import com.android.internal.R;
44 
45 import java.time.ZonedDateTime;
46 import java.util.Date;
47 import java.util.Locale;
48 
49 public class DataUsageController {
50 
51     private static final String TAG = "DataUsageController";
52     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
53     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
54     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
55     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
56             PERIOD_BUILDER, Locale.getDefault());
57 
58     private final Context mContext;
59     private final TelephonyManager mTelephonyManager;
60     private final ConnectivityManager mConnectivityManager;
61     private final INetworkStatsService mStatsService;
62     private final NetworkPolicyManager mPolicyManager;
63 
64     private INetworkStatsSession mSession;
65     private Callback mCallback;
66     private NetworkNameProvider mNetworkController;
67 
DataUsageController(Context context)68     public DataUsageController(Context context) {
69         mContext = context;
70         mTelephonyManager = TelephonyManager.from(context);
71         mConnectivityManager = ConnectivityManager.from(context);
72         mStatsService = INetworkStatsService.Stub.asInterface(
73                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
74         mPolicyManager = NetworkPolicyManager.from(mContext);
75     }
76 
setNetworkController(NetworkNameProvider networkController)77     public void setNetworkController(NetworkNameProvider networkController) {
78         mNetworkController = networkController;
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 
getSession()89     private INetworkStatsSession getSession() {
90         if (mSession == null) {
91             try {
92                 mSession = mStatsService.openSession();
93             } catch (RemoteException e) {
94                 Log.w(TAG, "Failed to open stats session", e);
95             } catch (RuntimeException e) {
96                 Log.w(TAG, "Failed to open stats session", e);
97             }
98         }
99         return mSession;
100     }
101 
setCallback(Callback callback)102     public void setCallback(Callback callback) {
103         mCallback = callback;
104     }
105 
warn(String msg)106     private DataUsageInfo warn(String msg) {
107         Log.w(TAG, "Failed to get data usage, " + msg);
108         return null;
109     }
110 
getDataUsageInfo()111     public DataUsageInfo getDataUsageInfo() {
112         final String subscriberId = getActiveSubscriberId(mContext);
113         if (subscriberId == null) {
114             return warn("no subscriber id");
115         }
116         NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
117         template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
118 
119         return getDataUsageInfo(template);
120     }
121 
getWifiDataUsageInfo()122     public DataUsageInfo getWifiDataUsageInfo() {
123         NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
124         return getDataUsageInfo(template);
125     }
126 
getDataUsageInfo(NetworkTemplate template)127     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
128         final INetworkStatsSession session = getSession();
129         if (session == null) {
130             return warn("no stats session");
131         }
132         final NetworkPolicy policy = findNetworkPolicy(template);
133         try {
134             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
135             final long now = System.currentTimeMillis();
136             final long start, end;
137             if (policy != null) {
138                 final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
139                         .cycleIterator(policy).next();
140                 start = cycle.first.toInstant().toEpochMilli();
141                 end = cycle.second.toInstant().toEpochMilli();
142             } else {
143                 // period = last 4 wks
144                 end = now;
145                 start = now - DateUtils.WEEK_IN_MILLIS * 4;
146             }
147             final long callStart = System.currentTimeMillis();
148             final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
149             final long callEnd = System.currentTimeMillis();
150             if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
151                     new Date(start), new Date(end), new Date(now), callEnd - callStart,
152                     historyEntryToString(entry)));
153             if (entry == null) {
154                 return warn("no entry data");
155             }
156             final long totalBytes = entry.rxBytes + entry.txBytes;
157             final DataUsageInfo usage = new DataUsageInfo();
158             usage.startDate = start;
159             usage.usageLevel = totalBytes;
160             usage.period = formatDateRange(start, end);
161             usage.cycleStart = start;
162             usage.cycleEnd = end;
163 
164             if (policy != null) {
165                 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
166                 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
167             } else {
168                 usage.warningLevel = getDefaultWarningLevel();
169             }
170             if (usage != null && mNetworkController != null) {
171                 usage.carrier = mNetworkController.getMobileDataNetworkName();
172             }
173             return usage;
174         } catch (RemoteException e) {
175             return warn("remote call failed");
176         }
177     }
178 
findNetworkPolicy(NetworkTemplate template)179     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
180         if (mPolicyManager == null || template == null) return null;
181         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
182         if (policies == null) return null;
183         final int N = policies.length;
184         for (int i = 0; i < N; i++) {
185             final NetworkPolicy policy = policies[i];
186             if (policy != null && template.equals(policy.template)) {
187                 return policy;
188             }
189         }
190         return null;
191     }
192 
historyEntryToString(NetworkStatsHistory.Entry entry)193     private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
194         return entry == null ? null : new StringBuilder("Entry[")
195                 .append("bucketDuration=").append(entry.bucketDuration)
196                 .append(",bucketStart=").append(entry.bucketStart)
197                 .append(",activeTime=").append(entry.activeTime)
198                 .append(",rxBytes=").append(entry.rxBytes)
199                 .append(",rxPackets=").append(entry.rxPackets)
200                 .append(",txBytes=").append(entry.txBytes)
201                 .append(",txPackets=").append(entry.txPackets)
202                 .append(",operations=").append(entry.operations)
203                 .append(']').toString();
204     }
205 
setMobileDataEnabled(boolean enabled)206     public void setMobileDataEnabled(boolean enabled) {
207         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
208         mTelephonyManager.setDataEnabled(enabled);
209         if (mCallback != null) {
210             mCallback.onMobileDataEnabled(enabled);
211         }
212     }
213 
isMobileDataSupported()214     public boolean isMobileDataSupported() {
215         // require both supported network and ready SIM
216         return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
217                 && mTelephonyManager.getSimState() == SIM_STATE_READY;
218     }
219 
isMobileDataEnabled()220     public boolean isMobileDataEnabled() {
221         return mTelephonyManager.getDataEnabled();
222     }
223 
getActiveSubscriberId(Context context)224     private static String getActiveSubscriberId(Context context) {
225         final TelephonyManager tele = TelephonyManager.from(context);
226         final String actualSubscriberId = tele.getSubscriberId(
227                 SubscriptionManager.getDefaultDataSubscriptionId());
228         return actualSubscriberId;
229     }
230 
formatDateRange(long start, long end)231     private String formatDateRange(long start, long end) {
232         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
233         synchronized (PERIOD_BUILDER) {
234             PERIOD_BUILDER.setLength(0);
235             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
236                     .toString();
237         }
238     }
239 
240     public interface NetworkNameProvider {
getMobileDataNetworkName()241         String getMobileDataNetworkName();
242     }
243 
244     public static class DataUsageInfo {
245         public String carrier;
246         public String period;
247         public long startDate;
248         public long limitLevel;
249         public long warningLevel;
250         public long usageLevel;
251         public long cycleStart;
252         public long cycleEnd;
253     }
254 
255     public interface Callback {
onMobileDataEnabled(boolean enabled)256         void onMobileDataEnabled(boolean enabled);
257     }
258 }
259